@evo-dev/core 0.0.1-alpha.4 → 0.0.1-alpha.5
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/config/index.js +3 -3
- package/dist/index.js +860 -612
- package/package.json +1 -1
- package/src/evolution/candidates/index.ts +314 -44
- package/src/evolution/evidence/session-memory/index.ts +1 -0
- package/src/evolution/evidence/session-memory/segment.ts +2 -2
- package/src/evolution/evidence/session-memory/storage.ts +123 -2
- package/src/evolution/evidence/session-memory/types.ts +3 -3
- package/src/evolution/index.ts +4 -0
- package/src/evolution/knowledge/index.ts +9 -74
- package/src/evolution/paths.ts +3 -0
- package/src/evolution/processor/distillation.ts +42 -33
- package/src/evolution/processor/process.ts +3 -74
- package/src/evolution/schema.ts +23 -1
- package/src/evolution/shared.ts +62 -2
package/dist/index.js
CHANGED
|
@@ -59,10 +59,10 @@ __export(exports_knowledge, {
|
|
|
59
59
|
assertOkfKnowledgePlanContract: () => assertOkfKnowledgePlanContract,
|
|
60
60
|
activateOkfKnowledgePlan: () => activateOkfKnowledgePlan
|
|
61
61
|
});
|
|
62
|
-
import { createHash as
|
|
62
|
+
import { createHash as createHash3 } from "node:crypto";
|
|
63
63
|
import { existsSync, readFileSync, readdirSync } from "node:fs";
|
|
64
|
-
import { mkdir as
|
|
65
|
-
import { dirname as
|
|
64
|
+
import { mkdir as mkdir3, readFile as readFile3, readdir as readdir4, rename, rm as rm2, stat as stat2, writeFile as writeFile3 } from "node:fs/promises";
|
|
65
|
+
import { dirname as dirname3, isAbsolute as isAbsolute2, join as join4, relative as relative2, resolve as resolve2 } from "node:path";
|
|
66
66
|
|
|
67
67
|
// packages/core/src/config/paths.ts
|
|
68
68
|
function resolveEvoDevPaths(homeDir = getHomeDir()) {
|
|
@@ -114,14 +114,19 @@ function stripTrailingSlash(path) {
|
|
|
114
114
|
// packages/core/src/evolution/candidates/index.ts
|
|
115
115
|
var exports_candidates = {};
|
|
116
116
|
__export(exports_candidates, {
|
|
117
|
+
withEvolutionReviewDecisionLock: () => withEvolutionReviewDecisionLock,
|
|
118
|
+
updateEvolutionRepoProposalReviewState: () => updateEvolutionRepoProposalReviewState,
|
|
117
119
|
updateEvolutionKnowledgeReviewState: () => updateEvolutionKnowledgeReviewState,
|
|
118
120
|
readEvolutionReviewSnapshot: () => readEvolutionReviewSnapshot,
|
|
121
|
+
readEvolutionRepoProposalById: () => readEvolutionRepoProposalById,
|
|
119
122
|
readEvolutionKnowledgeRecordById: () => readEvolutionKnowledgeRecordById,
|
|
123
|
+
listEvolutionKnowledgeReviewHistory: () => listEvolutionKnowledgeReviewHistory,
|
|
120
124
|
listEvolutionKnowledgeRecords: () => listEvolutionKnowledgeRecords,
|
|
121
125
|
listEvolutionEvosCases: () => listEvolutionEvosCases
|
|
122
126
|
});
|
|
123
|
-
import {
|
|
124
|
-
import {
|
|
127
|
+
import { createHash as createHash2 } from "node:crypto";
|
|
128
|
+
import { mkdir as mkdir2, readFile as readFile2, readdir as readdir3, rm, writeFile as writeFile2 } from "node:fs/promises";
|
|
129
|
+
import { dirname as dirname2, join as join3 } from "node:path";
|
|
125
130
|
|
|
126
131
|
// packages/core/src/utils/errors.ts
|
|
127
132
|
function isNotFoundError(error) {
|
|
@@ -223,6 +228,7 @@ import { readdir as readdir2 } from "node:fs/promises";
|
|
|
223
228
|
import { isAbsolute, relative, resolve } from "node:path";
|
|
224
229
|
var MAX_EVIDENCE_EVENTS = 200;
|
|
225
230
|
var MAX_TEXT_LENGTH = 600;
|
|
231
|
+
var MAX_PROPOSED_CHANGE_LENGTH = 16 * 1024;
|
|
226
232
|
var PROCESS_LOCK_STALE_MS = 5 * 60 * 1000;
|
|
227
233
|
var REVIEW_STATES = [
|
|
228
234
|
"auto-accepted",
|
|
@@ -524,7 +530,8 @@ function createEvolutionRepoProposal(input) {
|
|
|
524
530
|
plannedFiles: input.plannedFiles.map((file) => ({
|
|
525
531
|
relativePath: sanitizeRelativePath(file.relativePath),
|
|
526
532
|
action: file.action,
|
|
527
|
-
reason: sanitizeText(file.reason)
|
|
533
|
+
reason: sanitizeText(file.reason),
|
|
534
|
+
...file.proposedChange === undefined ? {} : { proposedChange: sanitizeProposedChange(file.proposedChange) }
|
|
528
535
|
}))
|
|
529
536
|
};
|
|
530
537
|
validateEvolutionRepoProposal(proposal);
|
|
@@ -555,6 +562,27 @@ function validateEvolutionRepoProposal(proposal) {
|
|
|
555
562
|
"deferred",
|
|
556
563
|
"applied"
|
|
557
564
|
]);
|
|
565
|
+
if (proposal.reviewStateChangedAt !== undefined && Number.isNaN(new Date(proposal.reviewStateChangedAt).getTime())) {
|
|
566
|
+
throw new Error("Repo proposal reviewStateChangedAt must be a valid timestamp.");
|
|
567
|
+
}
|
|
568
|
+
for (const file of proposal.plannedFiles) {
|
|
569
|
+
if (file.proposedChange !== undefined && file.proposedChange.trim() === "") {
|
|
570
|
+
throw new Error("Repo proposal proposedChange must not be empty.");
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
if (proposal.lastDecision !== undefined) {
|
|
574
|
+
assertEnum("lastDecision.state", proposal.lastDecision.state, [
|
|
575
|
+
"accepted",
|
|
576
|
+
"rejected",
|
|
577
|
+
"deferred"
|
|
578
|
+
]);
|
|
579
|
+
if (proposal.lastDecision.reason !== null && (proposal.lastDecision.reason.trim() === "" || proposal.lastDecision.reason.length > 500)) {
|
|
580
|
+
throw new Error("Repo proposal decision reason must be 1-500 characters.");
|
|
581
|
+
}
|
|
582
|
+
if (Number.isNaN(new Date(proposal.lastDecision.decidedAt).getTime())) {
|
|
583
|
+
throw new Error("Repo proposal decision timestamp must be valid.");
|
|
584
|
+
}
|
|
585
|
+
}
|
|
558
586
|
if (proposal.apply.autoApply !== false || proposal.apply.requiresExplicitCommand !== true) {
|
|
559
587
|
throw new Error("Repo proposal must require explicit apply.");
|
|
560
588
|
}
|
|
@@ -562,6 +590,9 @@ function validateEvolutionRepoProposal(proposal) {
|
|
|
562
590
|
assertProvenance(proposal.provenance);
|
|
563
591
|
assertNoForbiddenRawFields(proposal);
|
|
564
592
|
}
|
|
593
|
+
function hasConcreteRepoProposalChanges(proposal) {
|
|
594
|
+
return proposal.targetRepoPath !== null && proposal.targetRepoPath.trim() !== "" && proposal.plannedFiles.length > 0 && proposal.plannedFiles.every((file) => file.proposedChange !== undefined && file.proposedChange.trim() !== "");
|
|
595
|
+
}
|
|
565
596
|
function validateEvolutionReviewCandidate(candidate) {
|
|
566
597
|
if (!isRecord(candidate))
|
|
567
598
|
throw new Error("Review candidate must be an object.");
|
|
@@ -580,8 +611,14 @@ function validateEvolutionReviewCandidate(candidate) {
|
|
|
580
611
|
assertString("reviewCandidate.targetStore", candidate.targetStore);
|
|
581
612
|
assertString("reviewCandidate.targetPath", candidate.targetPath);
|
|
582
613
|
assertString("reviewCandidate.stableKey", candidate.stableKey);
|
|
583
|
-
|
|
584
|
-
|
|
614
|
+
assertEnum("reviewCandidate.reviewState", candidate.reviewState, [
|
|
615
|
+
"needs-human",
|
|
616
|
+
"accepted",
|
|
617
|
+
"rejected",
|
|
618
|
+
"deferred"
|
|
619
|
+
]);
|
|
620
|
+
if (candidate.reviewStateChangedAt !== undefined && Number.isNaN(new Date(candidate.reviewStateChangedAt).getTime())) {
|
|
621
|
+
throw new Error("Review candidate reviewStateChangedAt must be a valid timestamp.");
|
|
585
622
|
}
|
|
586
623
|
if (!Array.isArray(candidate.reasons))
|
|
587
624
|
throw new Error("Review candidate reasons must be an array.");
|
|
@@ -769,6 +806,12 @@ function assertNoForbiddenRawFields(value, path = "") {
|
|
|
769
806
|
function sanitizeText(value) {
|
|
770
807
|
return value.replace(SENSITIVE_TEXT_REPLACE_PATTERN, "[redacted]").replace(/\s+/g, " ").trim().slice(0, MAX_TEXT_LENGTH);
|
|
771
808
|
}
|
|
809
|
+
function sanitizeProposedChange(value) {
|
|
810
|
+
return value.replace(SENSITIVE_TEXT_REPLACE_PATTERN, "[redacted]").replaceAll(`\r
|
|
811
|
+
`, `
|
|
812
|
+
`).replaceAll("\r", `
|
|
813
|
+
`).trim().slice(0, MAX_PROPOSED_CHANGE_LENGTH);
|
|
814
|
+
}
|
|
772
815
|
function sanitizeId(value) {
|
|
773
816
|
return value.replace(/[^a-zA-Z0-9._/-]/g, "-").replace(/[\\/]+/g, "-").slice(0, 160) || "local";
|
|
774
817
|
}
|
|
@@ -824,12 +867,14 @@ function resolveEvolutionPaths(input) {
|
|
|
824
867
|
const projectKey = sanitizeStorageId2("projectKey", input.projectKey);
|
|
825
868
|
const runId = sanitizeStorageId2("runId", input.runId);
|
|
826
869
|
const evolutionStateDir = join2(paths.stateDir, "evolution");
|
|
870
|
+
const projectStateDir = join2(evolutionStateDir, projectKey);
|
|
827
871
|
const runStateDir = join2(evolutionStateDir, projectKey, runId);
|
|
828
872
|
const repoProposalsDir = join2(runStateDir, "proposals");
|
|
829
873
|
const reviewCandidatesDir = join2(runStateDir, "review-candidates");
|
|
830
874
|
const knowledgeProjectDir = join2(paths.knowledgeDir, projectKey);
|
|
831
875
|
const evosCasesProjectDir = join2(paths.evosCasesDir, projectKey);
|
|
832
876
|
assertPathDescendant(evolutionStateDir, runStateDir, "runStateDir");
|
|
877
|
+
assertPathDescendant(evolutionStateDir, projectStateDir, "projectStateDir");
|
|
833
878
|
assertPathDescendant(evolutionStateDir, reviewCandidatesDir, "reviewCandidatesDir");
|
|
834
879
|
assertPathDescendant(paths.knowledgeDir, knowledgeProjectDir, "knowledgeProjectDir");
|
|
835
880
|
assertPathDescendant(paths.evosCasesDir, evosCasesProjectDir, "evosCasesProjectDir");
|
|
@@ -847,12 +892,40 @@ function resolveEvolutionPaths(input) {
|
|
|
847
892
|
knowledgeProjectDir,
|
|
848
893
|
knowledgeRecordsDir: join2(knowledgeProjectDir, "records"),
|
|
849
894
|
knowledgeIndexPath: join2(knowledgeProjectDir, "index.json"),
|
|
895
|
+
knowledgeReviewHistoryPath: join2(projectStateDir, "knowledge-review-history.jsonl"),
|
|
850
896
|
evosCasesProjectDir,
|
|
851
897
|
evosIndexPath: paths.evosIndexPath
|
|
852
898
|
};
|
|
853
899
|
}
|
|
854
900
|
|
|
855
901
|
// packages/core/src/evolution/candidates/index.ts
|
|
902
|
+
var REVIEW_LOCK_WAIT_MS = 2000;
|
|
903
|
+
var REVIEW_LOCK_RETRY_MS = 10;
|
|
904
|
+
async function withEvolutionReviewDecisionLock(input, operation) {
|
|
905
|
+
const paths = resolveEvoDevPaths(input.homeDir);
|
|
906
|
+
const key = createHash2("sha256").update(`${input.kind}\x00${input.itemId}`).digest("hex");
|
|
907
|
+
const lockPath = join3(paths.stateDir, "evolution", ".review-locks", `${key}.lock`);
|
|
908
|
+
const deadline = Date.now() + REVIEW_LOCK_WAIT_MS;
|
|
909
|
+
await mkdir2(dirname2(lockPath), { recursive: true });
|
|
910
|
+
while (true) {
|
|
911
|
+
try {
|
|
912
|
+
await writeFile2(lockPath, `${JSON.stringify({ version: 1, kind: input.kind, acquiredAt: new Date().toISOString(), pid: process.pid })}
|
|
913
|
+
`, { encoding: "utf8", flag: "wx" });
|
|
914
|
+
break;
|
|
915
|
+
} catch (error) {
|
|
916
|
+
if (!isAlreadyExistsError(error))
|
|
917
|
+
throw error;
|
|
918
|
+
if (Date.now() >= deadline)
|
|
919
|
+
throw new Error("Review decision lock is busy or stale.");
|
|
920
|
+
await sleep(REVIEW_LOCK_RETRY_MS);
|
|
921
|
+
}
|
|
922
|
+
}
|
|
923
|
+
try {
|
|
924
|
+
return await operation();
|
|
925
|
+
} finally {
|
|
926
|
+
await rm(lockPath, { force: true });
|
|
927
|
+
}
|
|
928
|
+
}
|
|
856
929
|
async function readEvolutionReviewSnapshot(input) {
|
|
857
930
|
const paths = resolveEvoDevPaths(input.homeDir);
|
|
858
931
|
const projectKeys = input.projectKey === undefined ? await listEvolutionReviewProjectKeys(paths) : [sanitizeStorageId2("projectKey", input.projectKey)];
|
|
@@ -905,6 +978,32 @@ async function listEvolutionKnowledgeRecords(input) {
|
|
|
905
978
|
projectKey: input.projectKey
|
|
906
979
|
})).knowledgeRecords.sort((left, right) => left.id.localeCompare(right.id));
|
|
907
980
|
}
|
|
981
|
+
async function listEvolutionKnowledgeReviewHistory(input) {
|
|
982
|
+
const paths = resolveEvoDevPaths(input.homeDir);
|
|
983
|
+
const projectKeys = input.projectKey === undefined ? await listDirectoryNames(join3(paths.stateDir, "evolution")) : [sanitizeStorageId2("projectKey", input.projectKey)];
|
|
984
|
+
const records = [];
|
|
985
|
+
for (const projectKey of projectKeys) {
|
|
986
|
+
const historyPath = resolveEvolutionPaths({
|
|
987
|
+
homeDir: input.homeDir,
|
|
988
|
+
projectKey,
|
|
989
|
+
runId: "review"
|
|
990
|
+
}).knowledgeReviewHistoryPath;
|
|
991
|
+
if (!await pathExists(historyPath))
|
|
992
|
+
continue;
|
|
993
|
+
for (const line of (await readFile2(historyPath, "utf8")).split(`
|
|
994
|
+
`)) {
|
|
995
|
+
if (line.trim() === "")
|
|
996
|
+
continue;
|
|
997
|
+
records.push(parseKnowledgeReviewHistoryRecord(JSON.parse(line)));
|
|
998
|
+
}
|
|
999
|
+
}
|
|
1000
|
+
return records.sort((left, right) => {
|
|
1001
|
+
const changedAt = left.changedAt.localeCompare(right.changedAt);
|
|
1002
|
+
if (changedAt !== 0)
|
|
1003
|
+
return changedAt;
|
|
1004
|
+
return left.knowledgeId.localeCompare(right.knowledgeId);
|
|
1005
|
+
});
|
|
1006
|
+
}
|
|
908
1007
|
async function listEvolutionEvosCases(input) {
|
|
909
1008
|
const paths = resolveEvoDevPaths(input.homeDir);
|
|
910
1009
|
const projectKeys = input.projectKey === undefined ? await listDirectoryNames(paths.evosCasesDir) : [sanitizeStorageId2("projectKey", input.projectKey)];
|
|
@@ -955,44 +1054,186 @@ async function readEvolutionKnowledgeRecordById(input) {
|
|
|
955
1054
|
return matches[0];
|
|
956
1055
|
}
|
|
957
1056
|
async function updateEvolutionKnowledgeReviewState(input) {
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
authority: input.reviewState === "accepted" ? "reviewed" : "contextual",
|
|
963
|
-
runtime: {
|
|
964
|
-
...record.runtime,
|
|
965
|
-
canLoad: input.reviewState === "accepted",
|
|
966
|
-
hardBlocking: false
|
|
1057
|
+
return await withEvolutionReviewDecisionLock({ homeDir: input.homeDir, kind: "knowledge", itemId: input.knowledgeId }, async () => {
|
|
1058
|
+
const record = await readEvolutionKnowledgeRecordById(input);
|
|
1059
|
+
if (input.expectedReviewState !== undefined && record.reviewState !== input.expectedReviewState) {
|
|
1060
|
+
throw new Error("Knowledge review state changed before the decision was applied.");
|
|
967
1061
|
}
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
1062
|
+
const paths = resolveEvolutionPaths({
|
|
1063
|
+
homeDir: input.homeDir,
|
|
1064
|
+
projectKey: record.projectKey,
|
|
1065
|
+
runId: "review"
|
|
1066
|
+
});
|
|
1067
|
+
const path = join3(paths.knowledgeRecordsDir, `${record.id}.json`);
|
|
1068
|
+
if (record.reviewState === input.reviewState)
|
|
1069
|
+
return { path, record, changed: false };
|
|
1070
|
+
const changedAt = normalizeTimestamp2(input.now);
|
|
1071
|
+
const next = {
|
|
1072
|
+
...record,
|
|
1073
|
+
reviewState: input.reviewState,
|
|
1074
|
+
authority: input.reviewState === "accepted" ? "reviewed" : "contextual",
|
|
1075
|
+
runtime: {
|
|
1076
|
+
...record.runtime,
|
|
1077
|
+
canLoad: false,
|
|
1078
|
+
hardBlocking: false
|
|
1079
|
+
}
|
|
1080
|
+
};
|
|
1081
|
+
validateEvolutionKnowledgeRecord(next);
|
|
1082
|
+
await writeJsonFile(path, next, { overwrite: true });
|
|
1083
|
+
const allProjectKnowledgeRecords = await readJsonFiles(paths.knowledgeRecordsDir, parseKnowledgeRecord);
|
|
1084
|
+
await writeJsonFile(paths.knowledgeIndexPath, {
|
|
1085
|
+
version: 1,
|
|
1086
|
+
kind: "evolution-knowledge-index",
|
|
1087
|
+
projectKey: next.projectKey,
|
|
1088
|
+
updatedAt: changedAt,
|
|
1089
|
+
records: allProjectKnowledgeRecords.map((item) => ({
|
|
1090
|
+
id: item.id,
|
|
1091
|
+
kind: item.kind,
|
|
1092
|
+
title: item.title,
|
|
1093
|
+
roleTags: item.roleTags,
|
|
1094
|
+
tags: item.tags,
|
|
1095
|
+
reviewState: item.reviewState,
|
|
1096
|
+
authority: item.authority,
|
|
1097
|
+
confidence: item.confidence,
|
|
1098
|
+
runtime: item.runtime
|
|
1099
|
+
}))
|
|
1100
|
+
}, { overwrite: true });
|
|
1101
|
+
const history = {
|
|
1102
|
+
version: 1,
|
|
1103
|
+
kind: "evolution-knowledge-review-state-changed",
|
|
1104
|
+
knowledgeId: next.id,
|
|
1105
|
+
projectKey: next.projectKey,
|
|
1106
|
+
roleTags: uniqueSorted(next.roleTags),
|
|
1107
|
+
artifactCreatedAt: next.provenance.createdAt,
|
|
1108
|
+
previousReviewState: record.reviewState,
|
|
1109
|
+
nextReviewState: next.reviewState,
|
|
1110
|
+
changedAt,
|
|
1111
|
+
metadataOnly: true
|
|
1112
|
+
};
|
|
1113
|
+
await mkdir2(dirname2(paths.knowledgeReviewHistoryPath), { recursive: true });
|
|
1114
|
+
await writeFile2(paths.knowledgeReviewHistoryPath, `${JSON.stringify(history)}
|
|
1115
|
+
`, {
|
|
1116
|
+
encoding: "utf8",
|
|
1117
|
+
flag: "a"
|
|
1118
|
+
});
|
|
1119
|
+
return { path, record: next, changed: true };
|
|
1120
|
+
});
|
|
1121
|
+
}
|
|
1122
|
+
async function readEvolutionRepoProposalById(input) {
|
|
1123
|
+
const matches = (await readEvolutionReviewSnapshot({
|
|
971
1124
|
homeDir: input.homeDir,
|
|
972
|
-
projectKey:
|
|
973
|
-
|
|
1125
|
+
projectKey: input.projectKey
|
|
1126
|
+
})).repoProposals.filter((proposal) => proposal.id === input.proposalId);
|
|
1127
|
+
if (matches.length === 0)
|
|
1128
|
+
throw new Error(`Repo proposal not found: ${input.proposalId}`);
|
|
1129
|
+
if (matches.length > 1) {
|
|
1130
|
+
throw new Error(`Repo proposal id is ambiguous across runs: ${input.proposalId}`);
|
|
1131
|
+
}
|
|
1132
|
+
return matches[0];
|
|
1133
|
+
}
|
|
1134
|
+
async function updateEvolutionRepoProposalReviewState(input) {
|
|
1135
|
+
return await withEvolutionReviewDecisionLock({ homeDir: input.homeDir, kind: "repo-proposal", itemId: input.proposalId }, async () => {
|
|
1136
|
+
const record = await readEvolutionRepoProposalById(input);
|
|
1137
|
+
if (!hasConcreteRepoProposalChanges(record)) {
|
|
1138
|
+
throw new Error("Repo proposal has no concrete repository changes to review.");
|
|
1139
|
+
}
|
|
1140
|
+
const reason = input.reason?.trim();
|
|
1141
|
+
if (input.reviewState === "rejected" && reason === undefined) {
|
|
1142
|
+
throw new Error("Rejecting a repo proposal requires a reason.");
|
|
1143
|
+
}
|
|
1144
|
+
if (reason !== undefined && (reason === "" || reason.length > 500)) {
|
|
1145
|
+
throw new Error("Repo proposal review reason must be 1-500 characters.");
|
|
1146
|
+
}
|
|
1147
|
+
if (input.expectedReviewState !== undefined && record.reviewState !== input.expectedReviewState) {
|
|
1148
|
+
throw new Error("Repo proposal review state changed before the decision was applied.");
|
|
1149
|
+
}
|
|
1150
|
+
const paths = resolveEvolutionPaths({
|
|
1151
|
+
homeDir: input.homeDir,
|
|
1152
|
+
projectKey: record.projectKey,
|
|
1153
|
+
runId: record.provenance.runId
|
|
1154
|
+
});
|
|
1155
|
+
const path = join3(paths.repoProposalsDir, `${record.id}.json`);
|
|
1156
|
+
if (record.reviewState === input.reviewState)
|
|
1157
|
+
return { path, record, changed: false };
|
|
1158
|
+
const changedAt = normalizeTimestamp2(input.now);
|
|
1159
|
+
const next = {
|
|
1160
|
+
...record,
|
|
1161
|
+
reviewState: input.reviewState,
|
|
1162
|
+
reviewStateChangedAt: changedAt,
|
|
1163
|
+
lastDecision: {
|
|
1164
|
+
state: input.reviewState,
|
|
1165
|
+
reason: reason === undefined ? null : sanitizeText(reason),
|
|
1166
|
+
decidedAt: changedAt
|
|
1167
|
+
}
|
|
1168
|
+
};
|
|
1169
|
+
validateEvolutionRepoProposal(next);
|
|
1170
|
+
await writeJsonFile(path, next, { overwrite: true });
|
|
1171
|
+
const allRunProposals = await readJsonFiles(paths.repoProposalsDir, parseRepoProposal);
|
|
1172
|
+
await writeJsonFile(paths.repoProposalsIndexPath, {
|
|
1173
|
+
schemaVersion: 1,
|
|
1174
|
+
projectKey: next.projectKey,
|
|
1175
|
+
runId: next.provenance.runId,
|
|
1176
|
+
updatedAt: changedAt,
|
|
1177
|
+
proposals: allRunProposals.map((proposal) => ({
|
|
1178
|
+
id: proposal.id,
|
|
1179
|
+
kind: proposal.kind,
|
|
1180
|
+
title: proposal.title,
|
|
1181
|
+
reviewState: proposal.reviewState
|
|
1182
|
+
}))
|
|
1183
|
+
}, { overwrite: true });
|
|
1184
|
+
return { path, record: next, changed: true };
|
|
974
1185
|
});
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
1186
|
+
}
|
|
1187
|
+
function parseKnowledgeReviewHistoryRecord(value) {
|
|
1188
|
+
if (!isRecord2(value))
|
|
1189
|
+
throw new Error("Knowledge review history record must be an object.");
|
|
1190
|
+
const allowedKeys = new Set([
|
|
1191
|
+
"version",
|
|
1192
|
+
"kind",
|
|
1193
|
+
"knowledgeId",
|
|
1194
|
+
"projectKey",
|
|
1195
|
+
"roleTags",
|
|
1196
|
+
"artifactCreatedAt",
|
|
1197
|
+
"previousReviewState",
|
|
1198
|
+
"nextReviewState",
|
|
1199
|
+
"changedAt",
|
|
1200
|
+
"metadataOnly"
|
|
1201
|
+
]);
|
|
1202
|
+
if (Object.keys(value).some((key) => !allowedKeys.has(key))) {
|
|
1203
|
+
throw new Error("Knowledge review history record contains unsupported fields.");
|
|
1204
|
+
}
|
|
1205
|
+
if (value.version !== 1 || value.kind !== "evolution-knowledge-review-state-changed" || typeof value.knowledgeId !== "string" || typeof value.projectKey !== "string" || !Array.isArray(value.roleTags) || !value.roleTags.every((roleId) => typeof roleId === "string") || typeof value.artifactCreatedAt !== "string" || typeof value.previousReviewState !== "string" || !REVIEW_STATES.includes(value.previousReviewState) || typeof value.nextReviewState !== "string" || !REVIEW_STATES.includes(value.nextReviewState) || typeof value.changedAt !== "string" || value.metadataOnly !== true) {
|
|
1206
|
+
throw new Error("Invalid knowledge review history record.");
|
|
1207
|
+
}
|
|
1208
|
+
normalizeTimestamp2(value.artifactCreatedAt);
|
|
1209
|
+
normalizeTimestamp2(value.changedAt);
|
|
1210
|
+
return {
|
|
979
1211
|
version: 1,
|
|
980
|
-
kind: "evolution-knowledge-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
return
|
|
1212
|
+
kind: "evolution-knowledge-review-state-changed",
|
|
1213
|
+
knowledgeId: sanitizeId(value.knowledgeId),
|
|
1214
|
+
projectKey: sanitizeStorageId2("projectKey", value.projectKey),
|
|
1215
|
+
roleTags: uniqueSorted(value.roleTags.map(sanitizeId)),
|
|
1216
|
+
artifactCreatedAt: value.artifactCreatedAt,
|
|
1217
|
+
previousReviewState: value.previousReviewState,
|
|
1218
|
+
nextReviewState: value.nextReviewState,
|
|
1219
|
+
changedAt: value.changedAt,
|
|
1220
|
+
metadataOnly: true
|
|
1221
|
+
};
|
|
1222
|
+
}
|
|
1223
|
+
function normalizeTimestamp2(value) {
|
|
1224
|
+
const date = value === undefined ? new Date : value instanceof Date ? value : new Date(value);
|
|
1225
|
+
if (Number.isNaN(date.getTime()))
|
|
1226
|
+
throw new Error("Invalid knowledge review history timestamp.");
|
|
1227
|
+
return date.toISOString();
|
|
1228
|
+
}
|
|
1229
|
+
function isAlreadyExistsError(error) {
|
|
1230
|
+
return error instanceof Error && "code" in error && error.code === "EEXIST";
|
|
1231
|
+
}
|
|
1232
|
+
async function sleep(milliseconds) {
|
|
1233
|
+
await new Promise((resolve2) => setTimeout(resolve2, milliseconds));
|
|
1234
|
+
}
|
|
1235
|
+
function isRecord2(value) {
|
|
1236
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
996
1237
|
}
|
|
997
1238
|
|
|
998
1239
|
// packages/core/src/evolution/knowledge/index.ts
|
|
@@ -1072,8 +1313,8 @@ function resolveOkfKnowledgePaths(homeDir) {
|
|
|
1072
1313
|
async function ensureOkfKnowledgeBase(homeDir) {
|
|
1073
1314
|
const paths = resolveOkfKnowledgePaths(homeDir);
|
|
1074
1315
|
await ensureLocalKnowledgeGitRepository(paths.knowledgeDir);
|
|
1075
|
-
await
|
|
1076
|
-
await
|
|
1316
|
+
await mkdir3(paths.indexesDir, { recursive: true });
|
|
1317
|
+
await mkdir3(paths.tmpDir, { recursive: true });
|
|
1077
1318
|
const directories = [
|
|
1078
1319
|
{
|
|
1079
1320
|
path: "",
|
|
@@ -1219,7 +1460,7 @@ function validateOkfKnowledgePlanContract(plan, _options = {}) {
|
|
|
1219
1460
|
const add = (path, code, message, severity = "error") => {
|
|
1220
1461
|
findings.push({ path, code, severity, message });
|
|
1221
1462
|
};
|
|
1222
|
-
if (!
|
|
1463
|
+
if (!isRecord3(plan)) {
|
|
1223
1464
|
add("$", "plan.object", "Plan must be an object.");
|
|
1224
1465
|
return { ok: false, findings };
|
|
1225
1466
|
}
|
|
@@ -1255,7 +1496,7 @@ function validateOkfKnowledgePlanContract(plan, _options = {}) {
|
|
|
1255
1496
|
}
|
|
1256
1497
|
if (Array.isArray(plan.droppedSignals)) {
|
|
1257
1498
|
plan.droppedSignals.forEach((signal, index) => {
|
|
1258
|
-
if (!
|
|
1499
|
+
if (!isRecord3(signal))
|
|
1259
1500
|
add(`droppedSignals[${index}]`, "signal.object", "Dropped signal must be an object.");
|
|
1260
1501
|
if (!isNonEmptyString(signal?.evidenceRef)) {
|
|
1261
1502
|
add(`droppedSignals[${index}].evidenceRef`, "signal.evidenceRef", "Dropped signal evidenceRef is required.");
|
|
@@ -1268,7 +1509,7 @@ function validateOkfKnowledgePlanContract(plan, _options = {}) {
|
|
|
1268
1509
|
}
|
|
1269
1510
|
if (Array.isArray(plan.conflicts)) {
|
|
1270
1511
|
plan.conflicts.forEach((conflict, index) => {
|
|
1271
|
-
if (!
|
|
1512
|
+
if (!isRecord3(conflict))
|
|
1272
1513
|
add(`conflicts[${index}]`, "conflict.object", "Conflict must be an object.");
|
|
1273
1514
|
if (!isNonEmptyString(conflict?.candidateId)) {
|
|
1274
1515
|
add(`conflicts[${index}].candidateId`, "conflict.candidateId", "Conflict candidateId is required.");
|
|
@@ -1329,7 +1570,7 @@ function normalizePlanCandidate(value, path) {
|
|
|
1329
1570
|
return candidate;
|
|
1330
1571
|
}
|
|
1331
1572
|
function validateCandidateContract(candidate, path, evalSetIds, add) {
|
|
1332
|
-
if (!
|
|
1573
|
+
if (!isRecord3(candidate)) {
|
|
1333
1574
|
add(path, "candidate.object", "Candidate must be an object.");
|
|
1334
1575
|
return;
|
|
1335
1576
|
}
|
|
@@ -1401,7 +1642,7 @@ function validateCandidateContract(candidate, path, evalSetIds, add) {
|
|
|
1401
1642
|
if (!Array.isArray(candidate.evidenceRefs) || candidate.evidenceRefs.length === 0) {
|
|
1402
1643
|
add(`${path}.evidenceRefs`, "candidate.active.evidenceRefs", "Active writes require evidenceRefs.");
|
|
1403
1644
|
}
|
|
1404
|
-
const verification =
|
|
1645
|
+
const verification = isRecord3(candidate.bodySections) && Array.isArray(candidate.bodySections.verification) ? candidate.bodySections.verification : [];
|
|
1405
1646
|
if (verification.length === 0 && !isNonEmptyString(candidate.verificationNotApplicableReason)) {
|
|
1406
1647
|
add(`${path}.bodySections.verification`, "candidate.active.verification", "Active writes require verification or an explicit not-applicable reason.");
|
|
1407
1648
|
}
|
|
@@ -1581,7 +1822,7 @@ function normalizeEvalSetDecision(value) {
|
|
|
1581
1822
|
return normalized;
|
|
1582
1823
|
}
|
|
1583
1824
|
function assertRecordValue(value, path) {
|
|
1584
|
-
if (!
|
|
1825
|
+
if (!isRecord3(value))
|
|
1585
1826
|
throw new Error(`${path} must be an object.`);
|
|
1586
1827
|
return value;
|
|
1587
1828
|
}
|
|
@@ -1611,14 +1852,14 @@ function readScore(value, path) {
|
|
|
1611
1852
|
}
|
|
1612
1853
|
return value;
|
|
1613
1854
|
}
|
|
1614
|
-
function
|
|
1855
|
+
function isRecord3(value) {
|
|
1615
1856
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1616
1857
|
}
|
|
1617
1858
|
function isNonEmptyString(value) {
|
|
1618
1859
|
return typeof value === "string" && value.trim() !== "";
|
|
1619
1860
|
}
|
|
1620
1861
|
function validateEvidenceRefValue(evidenceRef, path, add) {
|
|
1621
|
-
if (!
|
|
1862
|
+
if (!isRecord3(evidenceRef)) {
|
|
1622
1863
|
add(path, "evidenceRef.object", "Evidence ref must be an object.");
|
|
1623
1864
|
return;
|
|
1624
1865
|
}
|
|
@@ -1635,13 +1876,13 @@ function validateEvidenceRefValue(evidenceRef, path, add) {
|
|
|
1635
1876
|
}
|
|
1636
1877
|
}
|
|
1637
1878
|
function validateEvalSetValue(evalSet, path, add) {
|
|
1638
|
-
if (!
|
|
1879
|
+
if (!isRecord3(evalSet)) {
|
|
1639
1880
|
add(path, "evalSet.object", "Eval set must be an object.");
|
|
1640
1881
|
return;
|
|
1641
1882
|
}
|
|
1642
1883
|
if (!isNonEmptyString(evalSet.id))
|
|
1643
1884
|
add(`${path}.id`, "evalSet.id", "Eval set id is required.");
|
|
1644
|
-
if (!
|
|
1885
|
+
if (!isRecord3(evalSet.target)) {
|
|
1645
1886
|
add(`${path}.target`, "evalSet.target", "Eval set target is required.");
|
|
1646
1887
|
} else {
|
|
1647
1888
|
if (!isNonEmptyString(evalSet.target.kind))
|
|
@@ -1657,7 +1898,7 @@ function validateEvalSetValue(evalSet, path, add) {
|
|
|
1657
1898
|
} else {
|
|
1658
1899
|
evalSet.cases.forEach((item, index) => {
|
|
1659
1900
|
const casePath = `${path}.cases[${index}]`;
|
|
1660
|
-
if (!
|
|
1901
|
+
if (!isRecord3(item)) {
|
|
1661
1902
|
add(casePath, "evalSet.case.object", "Eval set case must be an object.");
|
|
1662
1903
|
return;
|
|
1663
1904
|
}
|
|
@@ -1670,7 +1911,7 @@ function validateEvalSetValue(evalSet, path, add) {
|
|
|
1670
1911
|
}
|
|
1671
1912
|
});
|
|
1672
1913
|
}
|
|
1673
|
-
if (!
|
|
1914
|
+
if (!isRecord3(evalSet.privacy)) {
|
|
1674
1915
|
add(`${path}.privacy`, "evalSet.privacy", "Eval set privacy is required.");
|
|
1675
1916
|
} else {
|
|
1676
1917
|
if (evalSet.privacy.usesRawPrompt !== false)
|
|
@@ -1702,7 +1943,7 @@ function validateOverlayUpdatesValue(value, path, add) {
|
|
|
1702
1943
|
}
|
|
1703
1944
|
value.forEach((item, index) => {
|
|
1704
1945
|
const itemPath = `${path}[${index}]`;
|
|
1705
|
-
if (!
|
|
1946
|
+
if (!isRecord3(item)) {
|
|
1706
1947
|
add(itemPath, "overlayUpdate.object", "Overlay update must be an object.");
|
|
1707
1948
|
return;
|
|
1708
1949
|
}
|
|
@@ -1714,7 +1955,7 @@ function validateOverlayUpdatesValue(value, path, add) {
|
|
|
1714
1955
|
});
|
|
1715
1956
|
}
|
|
1716
1957
|
function validateScoresValue(value, path, add) {
|
|
1717
|
-
if (!
|
|
1958
|
+
if (!isRecord3(value)) {
|
|
1718
1959
|
add(path, "scores.object", "Scores must be an object.");
|
|
1719
1960
|
return;
|
|
1720
1961
|
}
|
|
@@ -1733,7 +1974,7 @@ function validateScoresValue(value, path, add) {
|
|
|
1733
1974
|
}
|
|
1734
1975
|
}
|
|
1735
1976
|
function validatePrivacyCheckValue(value, path, add) {
|
|
1736
|
-
if (!
|
|
1977
|
+
if (!isRecord3(value)) {
|
|
1737
1978
|
add(path, "privacy.object", "Privacy check must be an object.");
|
|
1738
1979
|
return;
|
|
1739
1980
|
}
|
|
@@ -1743,7 +1984,7 @@ function validatePrivacyCheckValue(value, path, add) {
|
|
|
1743
1984
|
}
|
|
1744
1985
|
}
|
|
1745
1986
|
function validateBodySectionsValue(value, path, add) {
|
|
1746
|
-
if (!
|
|
1987
|
+
if (!isRecord3(value)) {
|
|
1747
1988
|
add(path, "bodySections.object", "Body sections must be an object.");
|
|
1748
1989
|
return;
|
|
1749
1990
|
}
|
|
@@ -1772,7 +2013,7 @@ function validateTargetPathValue(value, path, requireMarkdown, add) {
|
|
|
1772
2013
|
}
|
|
1773
2014
|
}
|
|
1774
2015
|
function isBehaviorChangeCandidate(candidate) {
|
|
1775
|
-
if (!
|
|
2016
|
+
if (!isRecord3(candidate))
|
|
1776
2017
|
return false;
|
|
1777
2018
|
const kind = typeof candidate.kind === "string" ? candidate.kind : "";
|
|
1778
2019
|
const haystack = [
|
|
@@ -1892,10 +2133,10 @@ function decideOkfKnowledgeCandidate(candidate, context) {
|
|
|
1892
2133
|
if (highRiskRequiresHuman) {
|
|
1893
2134
|
return {
|
|
1894
2135
|
...sanitized,
|
|
1895
|
-
decision: "
|
|
2136
|
+
decision: "no_write",
|
|
1896
2137
|
scores,
|
|
1897
|
-
reviewState: "
|
|
1898
|
-
decisionReason: "
|
|
2138
|
+
reviewState: "auto-stored/unreviewed",
|
|
2139
|
+
decisionReason: "No write: high-risk knowledge is not eligible for automatic activation."
|
|
1899
2140
|
};
|
|
1900
2141
|
}
|
|
1901
2142
|
if (conflict?.kind === "duplicate") {
|
|
@@ -1911,10 +2152,10 @@ function decideOkfKnowledgeCandidate(candidate, context) {
|
|
|
1911
2152
|
if (reusable) {
|
|
1912
2153
|
return {
|
|
1913
2154
|
...sanitized,
|
|
1914
|
-
decision: "
|
|
2155
|
+
decision: "no_write",
|
|
1915
2156
|
scores,
|
|
1916
|
-
reviewState: "
|
|
1917
|
-
decisionReason: conflict?.reason ??
|
|
2157
|
+
reviewState: "auto-stored/unreviewed",
|
|
2158
|
+
decisionReason: conflict?.reason ?? explainAutomaticNoWriteDecision(sanitized, scores, hasVerification, hasScopeTags)
|
|
1918
2159
|
};
|
|
1919
2160
|
}
|
|
1920
2161
|
return {
|
|
@@ -1927,8 +2168,8 @@ function decideOkfKnowledgeCandidate(candidate, context) {
|
|
|
1927
2168
|
}
|
|
1928
2169
|
async function activateOkfKnowledgePlan(input) {
|
|
1929
2170
|
const validation = validateOkfKnowledgePlanContract(input.plan);
|
|
1930
|
-
const projectKey =
|
|
1931
|
-
const runId =
|
|
2171
|
+
const projectKey = isRecord3(input.plan) && isNonEmptyString(input.plan.projectKey) ? input.plan.projectKey : "unknown";
|
|
2172
|
+
const runId = isRecord3(input.plan) && isNonEmptyString(input.plan.runId) ? input.plan.runId : "unknown";
|
|
1932
2173
|
if (!validation.ok) {
|
|
1933
2174
|
await writeFailedOkfKnowledgePlanArtifact({
|
|
1934
2175
|
homeDir: input.homeDir,
|
|
@@ -1960,12 +2201,7 @@ async function activateOkfKnowledgePlan(input) {
|
|
|
1960
2201
|
continue;
|
|
1961
2202
|
}
|
|
1962
2203
|
if (candidate.decision === "needs-human") {
|
|
1963
|
-
|
|
1964
|
-
homeDir: input.homeDir,
|
|
1965
|
-
plan: input.plan,
|
|
1966
|
-
candidate
|
|
1967
|
-
});
|
|
1968
|
-
needsHumanCandidates.push(candidate.id);
|
|
2204
|
+
skippedCandidates.push(candidate.id);
|
|
1969
2205
|
continue;
|
|
1970
2206
|
}
|
|
1971
2207
|
if (candidate.targetStore !== "okf") {
|
|
@@ -1979,10 +2215,10 @@ async function activateOkfKnowledgePlan(input) {
|
|
|
1979
2215
|
await appendOkfLog(paths.okfDir, `**Skip**: Candidate \`${candidate.id}\` matched existing [${candidate.title}](${toOkfLink(paths.okfDir, targetPath)}).`);
|
|
1980
2216
|
continue;
|
|
1981
2217
|
}
|
|
1982
|
-
await
|
|
1983
|
-
await
|
|
2218
|
+
await mkdir3(dirname3(targetPath), { recursive: true });
|
|
2219
|
+
await writeFile3(targetPath, renderOkfConcept(candidate, input.plan), "utf8");
|
|
1984
2220
|
conceptPaths.push(targetPath);
|
|
1985
|
-
affectedDirectories.add(
|
|
2221
|
+
affectedDirectories.add(dirname3(targetPath));
|
|
1986
2222
|
for (const update of candidate.overlayUpdates) {
|
|
1987
2223
|
const overlayPath = resolveOkfTargetPath(paths.okfDir, update.targetPath);
|
|
1988
2224
|
await ensureOverlayConcept({
|
|
@@ -1994,7 +2230,7 @@ async function activateOkfKnowledgePlan(input) {
|
|
|
1994
2230
|
link: update.link
|
|
1995
2231
|
});
|
|
1996
2232
|
overlayPaths.push(overlayPath);
|
|
1997
|
-
affectedDirectories.add(
|
|
2233
|
+
affectedDirectories.add(dirname3(overlayPath));
|
|
1998
2234
|
}
|
|
1999
2235
|
}
|
|
2000
2236
|
const indexPaths = await regenerateOkfDirectoryIndexes(paths.okfDir);
|
|
@@ -2008,8 +2244,8 @@ async function activateOkfKnowledgePlan(input) {
|
|
|
2008
2244
|
skippedCandidates,
|
|
2009
2245
|
needsHumanCandidates
|
|
2010
2246
|
});
|
|
2011
|
-
await
|
|
2012
|
-
await
|
|
2247
|
+
await rm2(tmpRunDir, { recursive: true, force: true });
|
|
2248
|
+
await rm2(failedPlanPath, { force: true });
|
|
2013
2249
|
return {
|
|
2014
2250
|
planPath,
|
|
2015
2251
|
failedPlanPath,
|
|
@@ -2032,8 +2268,8 @@ async function activateOkfKnowledgePlan(input) {
|
|
|
2032
2268
|
error: error instanceof Error ? error.message : String(error),
|
|
2033
2269
|
failureKind: "organizer"
|
|
2034
2270
|
});
|
|
2035
|
-
await
|
|
2036
|
-
await writeJson(join4(
|
|
2271
|
+
await rm2(tmpRunDir, { recursive: true, force: true });
|
|
2272
|
+
await writeJson(join4(dirname3(failedPlanPath), "organizer-report.json"), {
|
|
2037
2273
|
schemaVersion: 1,
|
|
2038
2274
|
kind: "okf-organizer-report",
|
|
2039
2275
|
projectKey: input.plan.projectKey,
|
|
@@ -2079,7 +2315,7 @@ async function writeFailedOkfKnowledgePlanArtifact(input) {
|
|
|
2079
2315
|
async function readFailedOkfKnowledgePlanArtifact(input) {
|
|
2080
2316
|
const path = resolveFailedPlanPath(input.homeDir, input.projectKey, input.runId);
|
|
2081
2317
|
const value = JSON.parse(await readFile3(path, "utf8"));
|
|
2082
|
-
if (
|
|
2318
|
+
if (isRecord3(value) && value.kind === "okf-knowledge-failed-plan") {
|
|
2083
2319
|
const artifact = value;
|
|
2084
2320
|
validateFailedPlanArtifact(artifact);
|
|
2085
2321
|
return { path, artifact };
|
|
@@ -2113,12 +2349,12 @@ async function resumeFailedOkfKnowledgePlan(input) {
|
|
|
2113
2349
|
}
|
|
2114
2350
|
async function discardFailedOkfKnowledgePlan(input) {
|
|
2115
2351
|
const path = resolveFailedPlanPath(input.homeDir, input.projectKey, input.runId);
|
|
2116
|
-
await
|
|
2352
|
+
await rm2(path, { force: true });
|
|
2117
2353
|
return { path };
|
|
2118
2354
|
}
|
|
2119
2355
|
async function rebuildOkfKnowledgeIndexes(input) {
|
|
2120
2356
|
const paths = resolveOkfKnowledgePaths(input.homeDir);
|
|
2121
|
-
await
|
|
2357
|
+
await mkdir3(paths.indexesDir, { recursive: true });
|
|
2122
2358
|
const concepts = (await listOkfKnowledgeConcepts({ homeDir: input.homeDir })).filter(isActiveOkfConcept);
|
|
2123
2359
|
const conceptSummaries = concepts.map((concept) => ({
|
|
2124
2360
|
id: concept.id,
|
|
@@ -2703,7 +2939,7 @@ async function writeConceptLifecycleFrontmatter(input) {
|
|
|
2703
2939
|
if (parsed === null)
|
|
2704
2940
|
throw new Error(`OKF concept missing frontmatter: ${input.path}`);
|
|
2705
2941
|
const frontmatter = upsertOkfLifecycleFrontmatter(parsed.frontmatter, input.reviewState, input.lifecycle);
|
|
2706
|
-
await
|
|
2942
|
+
await writeFile3(input.path, `---
|
|
2707
2943
|
${frontmatter.trimEnd()}
|
|
2708
2944
|
---
|
|
2709
2945
|
${parsed.body}`, "utf8");
|
|
@@ -2749,7 +2985,7 @@ function upsertOkfLifecycleFrontmatter(frontmatter, reviewState, lifecycle) {
|
|
|
2749
2985
|
}
|
|
2750
2986
|
async function appendLifecycleLogs(input) {
|
|
2751
2987
|
const directories = [
|
|
2752
|
-
...new Set(input.conceptPaths.flatMap((path) => ancestorDirectories(input.okfDir,
|
|
2988
|
+
...new Set(input.conceptPaths.flatMap((path) => ancestorDirectories(input.okfDir, dirname3(path))))
|
|
2753
2989
|
];
|
|
2754
2990
|
const logPaths = [];
|
|
2755
2991
|
for (const directory of directories) {
|
|
@@ -2965,15 +3201,15 @@ function resolveFailedPlanPath(homeDir, projectKey, runId) {
|
|
|
2965
3201
|
return path;
|
|
2966
3202
|
}
|
|
2967
3203
|
async function ensureLocalKnowledgeGitRepository(knowledgeDir) {
|
|
2968
|
-
await
|
|
3204
|
+
await mkdir3(knowledgeDir, { recursive: true });
|
|
2969
3205
|
const gitDir = join4(knowledgeDir, ".git");
|
|
2970
3206
|
if (await pathExists2(gitDir))
|
|
2971
3207
|
return;
|
|
2972
|
-
await
|
|
2973
|
-
await
|
|
2974
|
-
await
|
|
2975
|
-
await
|
|
2976
|
-
await
|
|
3208
|
+
await mkdir3(join4(gitDir, "objects", "info"), { recursive: true });
|
|
3209
|
+
await mkdir3(join4(gitDir, "objects", "pack"), { recursive: true });
|
|
3210
|
+
await mkdir3(join4(gitDir, "refs", "heads"), { recursive: true });
|
|
3211
|
+
await mkdir3(join4(gitDir, "refs", "tags"), { recursive: true });
|
|
3212
|
+
await mkdir3(join4(gitDir, "info"), { recursive: true });
|
|
2977
3213
|
await writeTextIfMissing(join4(gitDir, "HEAD"), `ref: refs/heads/main
|
|
2978
3214
|
`);
|
|
2979
3215
|
await writeTextIfMissing(join4(gitDir, "config"), [
|
|
@@ -3016,8 +3252,8 @@ function assertPathDescendant2(root, candidate, field) {
|
|
|
3016
3252
|
async function writeTextIfMissing(path, value) {
|
|
3017
3253
|
if (await pathExists2(path))
|
|
3018
3254
|
return;
|
|
3019
|
-
await
|
|
3020
|
-
await
|
|
3255
|
+
await mkdir3(dirname3(path), { recursive: true });
|
|
3256
|
+
await writeFile3(path, value, { encoding: "utf8", flag: "wx" });
|
|
3021
3257
|
}
|
|
3022
3258
|
function createCandidateFromEvosCase(evosCase) {
|
|
3023
3259
|
const targetPath = `concepts/evos/${evosCase.id}.md`;
|
|
@@ -3078,49 +3314,6 @@ function createOverlayUpdates(projectKey, roleTags, workflowTags, sourceLink) {
|
|
|
3078
3314
|
}))
|
|
3079
3315
|
];
|
|
3080
3316
|
}
|
|
3081
|
-
async function writeNeedsHumanKnowledgeCandidate(input) {
|
|
3082
|
-
const root = join4(resolveEvoDevPaths(input.homeDir).stateDir, "evolution");
|
|
3083
|
-
const targetDir = join4(root, sanitizePlanStorageId("projectKey", input.plan.projectKey), sanitizePlanStorageId("runId", input.plan.runId), "review-candidates");
|
|
3084
|
-
const targetPath = join4(targetDir, `${sanitizePlanStorageId("candidateId", input.candidate.id)}.json`);
|
|
3085
|
-
assertPathDescendant2(root, targetPath, "reviewCandidatePath");
|
|
3086
|
-
const candidateSnapshot = sanitizeReviewQueueValue(input.candidate);
|
|
3087
|
-
await writeJson(targetPath, {
|
|
3088
|
-
schemaVersion: 1,
|
|
3089
|
-
kind: "evolution-review-candidate",
|
|
3090
|
-
id: input.candidate.id,
|
|
3091
|
-
projectKey: input.plan.projectKey,
|
|
3092
|
-
runId: input.plan.runId,
|
|
3093
|
-
createdAt: input.plan.createdAt,
|
|
3094
|
-
candidateKind: input.candidate.kind,
|
|
3095
|
-
title: sanitizeOkfText(input.candidate.title),
|
|
3096
|
-
targetStore: input.candidate.targetStore,
|
|
3097
|
-
targetPath: sanitizeOkfText(input.candidate.targetPath),
|
|
3098
|
-
stableKey: sanitizeOkfText(input.candidate.stableKey),
|
|
3099
|
-
reviewState: "needs-human",
|
|
3100
|
-
reasons: [sanitizeOkfText(input.candidate.decisionReason)],
|
|
3101
|
-
candidate: candidateSnapshot,
|
|
3102
|
-
provenance: {
|
|
3103
|
-
runId: input.plan.runId,
|
|
3104
|
-
evidenceWindowId: input.plan.evidenceWindowId,
|
|
3105
|
-
evidenceRefs: input.candidate.evidenceRefs.map(sanitizeOkfText),
|
|
3106
|
-
createdBy: "evodev",
|
|
3107
|
-
rawLogsStored: false,
|
|
3108
|
-
rawPromptsStored: false,
|
|
3109
|
-
sourceDumpsStored: false,
|
|
3110
|
-
rawCommandOutputStored: false
|
|
3111
|
-
},
|
|
3112
|
-
privacy: {
|
|
3113
|
-
classification: "local-private",
|
|
3114
|
-
rawPromptsStored: false,
|
|
3115
|
-
rawLogsStored: false,
|
|
3116
|
-
sourceDumpsStored: false,
|
|
3117
|
-
rawCommandOutputStored: false,
|
|
3118
|
-
secretsStored: false,
|
|
3119
|
-
internalLinksStored: false
|
|
3120
|
-
}
|
|
3121
|
-
}, { overwrite: true });
|
|
3122
|
-
return targetPath;
|
|
3123
|
-
}
|
|
3124
3317
|
function sanitizeOkfPlanCandidate(candidate) {
|
|
3125
3318
|
return sanitizeReviewQueueValue(candidate);
|
|
3126
3319
|
}
|
|
@@ -3143,9 +3336,9 @@ function hasUnsafeCandidateContent(candidate) {
|
|
|
3143
3336
|
return FORBIDDEN_OKF_TEXT.test(content) || FORBIDDEN_OKF_FIELD.test(content) || PRIVATE_OR_INTERNAL_URL.test(content) || candidate.privacyCheck !== undefined && Object.values(candidate.privacyCheck).some((value) => value !== false);
|
|
3144
3337
|
}
|
|
3145
3338
|
function hasHighRiskHumanReviewSignal(candidate) {
|
|
3146
|
-
if (!
|
|
3339
|
+
if (!isRecord3(candidate))
|
|
3147
3340
|
return false;
|
|
3148
|
-
const body =
|
|
3341
|
+
const body = isRecord3(candidate.bodySections) ? candidate.bodySections : {};
|
|
3149
3342
|
const stringArray = (value) => Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
|
|
3150
3343
|
const stringValue = (value) => typeof value === "string" ? value : "";
|
|
3151
3344
|
const values = [
|
|
@@ -3174,7 +3367,7 @@ function hasHighRiskHumanReviewSignal(candidate) {
|
|
|
3174
3367
|
];
|
|
3175
3368
|
return HUMAN_REVIEW_DOMAIN_PATTERN.test(values.join(" "));
|
|
3176
3369
|
}
|
|
3177
|
-
function
|
|
3370
|
+
function explainAutomaticNoWriteDecision(candidate, scores, hasVerification, hasScopeTags) {
|
|
3178
3371
|
const reasons = [];
|
|
3179
3372
|
if (candidate.targetStore !== "okf")
|
|
3180
3373
|
reasons.push("target is not user-local OKF");
|
|
@@ -3194,7 +3387,7 @@ function explainNeedsHumanDecision(candidate, scores, hasVerification, hasScopeT
|
|
|
3194
3387
|
reasons.push("stability is below auto-accept");
|
|
3195
3388
|
if (scores.duplicationRisk > 2)
|
|
3196
3389
|
reasons.push("duplication risk is above auto-accept");
|
|
3197
|
-
return `
|
|
3390
|
+
return `No write: ${reasons.join("; ") || "auto-accept gates were not all satisfied"}.`;
|
|
3198
3391
|
}
|
|
3199
3392
|
function resolveCandidateReviewStateForWrite(candidate) {
|
|
3200
3393
|
if (candidate.reviewState === "auto-accepted" || candidate.decision === "auto-accept") {
|
|
@@ -3259,19 +3452,19 @@ async function ensureOverlayConcept(input) {
|
|
|
3259
3452
|
const root = segments[0] ?? "repos";
|
|
3260
3453
|
const scopeId = segments[1] ?? input.projectKey;
|
|
3261
3454
|
const type = root === "roles" ? "EvoDev Role Attention" : root === "workflows" ? "EvoDev Workflow Attention" : "EvoDev Repo Attention";
|
|
3262
|
-
await ensureOkfDirectory(input.okfDir,
|
|
3455
|
+
await ensureOkfDirectory(input.okfDir, dirname3(relativePath), scopeId, `Attention overlay for ${scopeId}.`);
|
|
3263
3456
|
const exists = await pathExists2(input.overlayPath);
|
|
3264
3457
|
const linkLine = `- [${input.candidate.title}](${input.link}) - ${input.candidate.description}`;
|
|
3265
3458
|
if (exists) {
|
|
3266
3459
|
const current = await readFile3(input.overlayPath, "utf8");
|
|
3267
3460
|
if (current.includes(input.link))
|
|
3268
3461
|
return;
|
|
3269
|
-
await
|
|
3462
|
+
await writeFile3(input.overlayPath, `${current.trimEnd()}
|
|
3270
3463
|
${linkLine}
|
|
3271
3464
|
`, "utf8");
|
|
3272
3465
|
return;
|
|
3273
3466
|
}
|
|
3274
|
-
await
|
|
3467
|
+
await writeFile3(input.overlayPath, [
|
|
3275
3468
|
"---",
|
|
3276
3469
|
`type: ${yamlString(type)}`,
|
|
3277
3470
|
`title: ${yamlString(`${scopeId} attention`)}`,
|
|
@@ -3416,11 +3609,11 @@ function renderOkfConcept(candidate, plan) {
|
|
|
3416
3609
|
}
|
|
3417
3610
|
async function ensureOkfDirectory(okfDir, relativeDir, title, description) {
|
|
3418
3611
|
const dir = relativeDir === "" || relativeDir === "." ? okfDir : resolveOkfTargetPath(okfDir, relativeDir);
|
|
3419
|
-
await
|
|
3612
|
+
await mkdir3(dir, { recursive: true });
|
|
3420
3613
|
const isRoot = dir === okfDir;
|
|
3421
3614
|
const indexPath = join4(dir, "index.md");
|
|
3422
3615
|
if (!await pathExists2(indexPath)) {
|
|
3423
|
-
await
|
|
3616
|
+
await writeFile3(indexPath, isRoot ? [
|
|
3424
3617
|
"---",
|
|
3425
3618
|
`okf_version: ${yamlString("0.1")}`,
|
|
3426
3619
|
`title: ${yamlString(title)}`,
|
|
@@ -3437,7 +3630,7 @@ async function ensureOkfDirectory(okfDir, relativeDir, title, description) {
|
|
|
3437
3630
|
}
|
|
3438
3631
|
const logPath = join4(dir, "log.md");
|
|
3439
3632
|
if (!await pathExists2(logPath)) {
|
|
3440
|
-
await
|
|
3633
|
+
await writeFile3(logPath, [
|
|
3441
3634
|
"# Directory Update Log",
|
|
3442
3635
|
"",
|
|
3443
3636
|
`## ${todayIsoDate()}`,
|
|
@@ -3488,7 +3681,7 @@ async function regenerateOkfDirectoryIndexes(okfDir) {
|
|
|
3488
3681
|
].join(`
|
|
3489
3682
|
`);
|
|
3490
3683
|
const indexPath = join4(directory, "index.md");
|
|
3491
|
-
await
|
|
3684
|
+
await writeFile3(indexPath, content, "utf8");
|
|
3492
3685
|
paths.push(indexPath);
|
|
3493
3686
|
}
|
|
3494
3687
|
return paths;
|
|
@@ -3510,7 +3703,7 @@ function ancestorDirectories(root, directory) {
|
|
|
3510
3703
|
const directories = [directory];
|
|
3511
3704
|
let current = directory;
|
|
3512
3705
|
while (current !== root && current.startsWith(root)) {
|
|
3513
|
-
current =
|
|
3706
|
+
current = dirname3(current);
|
|
3514
3707
|
directories.push(current);
|
|
3515
3708
|
}
|
|
3516
3709
|
return directories;
|
|
@@ -3519,20 +3712,20 @@ async function appendOkfLog(okfDir, entry) {
|
|
|
3519
3712
|
await prependLogEntry(join4(okfDir, "log.md"), entry);
|
|
3520
3713
|
}
|
|
3521
3714
|
async function prependLogEntry(logPath, entry) {
|
|
3522
|
-
await
|
|
3715
|
+
await mkdir3(dirname3(logPath), { recursive: true });
|
|
3523
3716
|
const date = todayIsoDate();
|
|
3524
3717
|
const existing = await pathExists2(logPath) ? await readFile3(logPath, "utf8") : `# Directory Update Log
|
|
3525
3718
|
`;
|
|
3526
3719
|
const line = `* ${entry}`;
|
|
3527
3720
|
if (existing.includes(`## ${date}`)) {
|
|
3528
|
-
await
|
|
3721
|
+
await writeFile3(logPath, existing.replace(`## ${date}
|
|
3529
3722
|
`, `## ${date}
|
|
3530
3723
|
${line}
|
|
3531
3724
|
`), "utf8");
|
|
3532
3725
|
return;
|
|
3533
3726
|
}
|
|
3534
3727
|
const withoutTitle = existing.replace(/^# Directory Update Log\s*/u, "").trimStart();
|
|
3535
|
-
await
|
|
3728
|
+
await writeFile3(logPath, `${["# Directory Update Log", "", `## ${date}`, line, "", withoutTitle].join(`
|
|
3536
3729
|
`).trimEnd()}
|
|
3537
3730
|
`, "utf8");
|
|
3538
3731
|
}
|
|
@@ -3624,7 +3817,7 @@ function createKnowledgeContextRevision(items) {
|
|
|
3624
3817
|
}))))}`;
|
|
3625
3818
|
}
|
|
3626
3819
|
function sha256Short2(value) {
|
|
3627
|
-
return
|
|
3820
|
+
return createHash3("sha256").update(value).digest("hex").slice(0, 16);
|
|
3628
3821
|
}
|
|
3629
3822
|
function stableJsonStringify(value) {
|
|
3630
3823
|
if (value === null || typeof value !== "object")
|
|
@@ -3967,7 +4160,7 @@ function validateOkfKnowledgePlan(plan) {
|
|
|
3967
4160
|
assertOkfKnowledgePlanContract(plan);
|
|
3968
4161
|
}
|
|
3969
4162
|
function validateFailedPlanArtifact(artifact) {
|
|
3970
|
-
if (!
|
|
4163
|
+
if (!isRecord3(artifact))
|
|
3971
4164
|
throw new Error("Failed plan artifact must be an object.");
|
|
3972
4165
|
if (artifact.schemaVersion !== 1)
|
|
3973
4166
|
throw new Error("Failed plan artifact schemaVersion must be 1.");
|
|
@@ -3987,7 +4180,7 @@ function validateFailedPlanArtifact(artifact) {
|
|
|
3987
4180
|
if (!Array.isArray(artifact.findings))
|
|
3988
4181
|
throw new Error("Failed plan artifact findings must be an array.");
|
|
3989
4182
|
for (const finding of artifact.findings) {
|
|
3990
|
-
if (!
|
|
4183
|
+
if (!isRecord3(finding))
|
|
3991
4184
|
throw new Error("Failed plan finding must be an object.");
|
|
3992
4185
|
if (!isNonEmptyString(finding.path) || !isNonEmptyString(finding.code) || !isNonEmptyString(finding.message)) {
|
|
3993
4186
|
throw new Error("Failed plan finding fields are required.");
|
|
@@ -4346,9 +4539,9 @@ function todayIsoDate() {
|
|
|
4346
4539
|
return new Date().toISOString().slice(0, 10);
|
|
4347
4540
|
}
|
|
4348
4541
|
async function writeJson(path, value, options = {}) {
|
|
4349
|
-
await
|
|
4542
|
+
await mkdir3(dirname3(path), { recursive: true });
|
|
4350
4543
|
const flag = options.overwrite === true ? "w" : "wx";
|
|
4351
|
-
await
|
|
4544
|
+
await writeFile3(path, `${JSON.stringify(value, null, 2)}
|
|
4352
4545
|
`, { encoding: "utf8", flag });
|
|
4353
4546
|
}
|
|
4354
4547
|
async function pathExists2(path) {
|
|
@@ -4399,7 +4592,7 @@ function applyStrictestAgentPermissions(...permissions) {
|
|
|
4399
4592
|
return merged;
|
|
4400
4593
|
}
|
|
4401
4594
|
function parseAgentProfile(value) {
|
|
4402
|
-
if (!
|
|
4595
|
+
if (!isRecord4(value))
|
|
4403
4596
|
throw new Error("Agent profile must be an object.");
|
|
4404
4597
|
if (value.version !== 1)
|
|
4405
4598
|
throw new Error("Agent profile version must be 1.");
|
|
@@ -4534,7 +4727,7 @@ function validateAgentOutput(schema, output) {
|
|
|
4534
4727
|
if (schema === "design-options-v1")
|
|
4535
4728
|
return validateRequiredObject(output, ["summary", "options", "recommendation", "evidenceRefs"]);
|
|
4536
4729
|
const errors2 = [];
|
|
4537
|
-
if (!
|
|
4730
|
+
if (!isRecord4(output))
|
|
4538
4731
|
return { ok: false, errors: ["Output must be an object."] };
|
|
4539
4732
|
if (typeof output.summary !== "string")
|
|
4540
4733
|
errors2.push("summary is required.");
|
|
@@ -4707,7 +4900,7 @@ function collectPrivacyBoundaryAdvisories(contract, lenses, workflowId) {
|
|
|
4707
4900
|
] : [];
|
|
4708
4901
|
}
|
|
4709
4902
|
function validateRequiredObject(output, fields) {
|
|
4710
|
-
if (!
|
|
4903
|
+
if (!isRecord4(output))
|
|
4711
4904
|
return { ok: false, errors: ["Output must be an object."] };
|
|
4712
4905
|
const errors2 = fields.filter((field) => output[field] === undefined).map((field) => `${field} is required.`);
|
|
4713
4906
|
return { ok: errors2.length === 0, errors: errors2 };
|
|
@@ -4733,7 +4926,7 @@ function assertStringArray(value, path) {
|
|
|
4733
4926
|
throw new Error(`${path} must be an array of non-empty strings.`);
|
|
4734
4927
|
}
|
|
4735
4928
|
}
|
|
4736
|
-
function
|
|
4929
|
+
function isRecord4(value) {
|
|
4737
4930
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
4738
4931
|
}
|
|
4739
4932
|
// packages/core/src/assets/errors.ts
|
|
@@ -4948,8 +5141,8 @@ __export(exports_code_agent_traces, {
|
|
|
4948
5141
|
listCodeAgentTraceRefs: () => listCodeAgentTraceRefs,
|
|
4949
5142
|
createCodeAgentTraceRef: () => createCodeAgentTraceRef
|
|
4950
5143
|
});
|
|
4951
|
-
import { mkdir as
|
|
4952
|
-
import { dirname as
|
|
5144
|
+
import { mkdir as mkdir5, readFile as readFile6, readdir as readdir6, stat as stat4, writeFile as writeFile4 } from "node:fs/promises";
|
|
5145
|
+
import { dirname as dirname5, isAbsolute as isAbsolute4, join as join7, normalize, relative as relative5 } from "node:path";
|
|
4953
5146
|
|
|
4954
5147
|
// packages/core/src/runtime-logs/index.ts
|
|
4955
5148
|
var exports_runtime_logs = {};
|
|
@@ -4967,8 +5160,8 @@ __export(exports_runtime_logs, {
|
|
|
4967
5160
|
appendDebugLogEntry: () => appendDebugLogEntry,
|
|
4968
5161
|
appendCliLogEntry: () => appendCliLogEntry
|
|
4969
5162
|
});
|
|
4970
|
-
import { appendFile, mkdir as
|
|
4971
|
-
import { dirname as
|
|
5163
|
+
import { appendFile, mkdir as mkdir4 } from "node:fs/promises";
|
|
5164
|
+
import { dirname as dirname4, isAbsolute as isAbsolute3, join as join6, relative as relative4 } from "node:path";
|
|
4972
5165
|
var MAX_LOG_STRING_LENGTH = 4096;
|
|
4973
5166
|
var MAX_EVENT_STRING_LENGTH = 512;
|
|
4974
5167
|
var MAX_DEBUG_ARRAY_LENGTH = 50;
|
|
@@ -5234,7 +5427,7 @@ function resolveProjectLogKey(homeDir, repoRoot) {
|
|
|
5234
5427
|
return sanitizePersistentIdentifier(projectKey, "project");
|
|
5235
5428
|
}
|
|
5236
5429
|
function resolveTraceSessionKey(payload) {
|
|
5237
|
-
const record =
|
|
5430
|
+
const record = isRecord5(payload) ? payload : {};
|
|
5238
5431
|
const sessionId = optionalString3(record.session_id ?? record.sessionId);
|
|
5239
5432
|
const cwd = optionalString3(record.cwd);
|
|
5240
5433
|
const source = sessionId ?? cwd ?? "local";
|
|
@@ -5299,7 +5492,7 @@ function sanitizeEvoDevExecutionEventForAppend(event) {
|
|
|
5299
5492
|
});
|
|
5300
5493
|
}
|
|
5301
5494
|
async function appendJsonLine(path, value) {
|
|
5302
|
-
await
|
|
5495
|
+
await mkdir4(dirname4(path), { recursive: true });
|
|
5303
5496
|
await appendFile(path, `${JSON.stringify(value)}
|
|
5304
5497
|
`, "utf8");
|
|
5305
5498
|
}
|
|
@@ -5380,7 +5573,7 @@ function sanitizeDebugValue(value, depth = 0) {
|
|
|
5380
5573
|
}
|
|
5381
5574
|
return items;
|
|
5382
5575
|
}
|
|
5383
|
-
if (
|
|
5576
|
+
if (isRecord5(value)) {
|
|
5384
5577
|
const output = {};
|
|
5385
5578
|
const entries = Object.entries(value).slice(0, MAX_DEBUG_OBJECT_KEYS);
|
|
5386
5579
|
for (const [key, nestedValue] of entries) {
|
|
@@ -5416,7 +5609,7 @@ function describeError(error) {
|
|
|
5416
5609
|
return truncateString(error.message);
|
|
5417
5610
|
return truncateString(String(error));
|
|
5418
5611
|
}
|
|
5419
|
-
function
|
|
5612
|
+
function isRecord5(value) {
|
|
5420
5613
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
5421
5614
|
}
|
|
5422
5615
|
|
|
@@ -5469,7 +5662,7 @@ function createCodeAgentTraceRef(input) {
|
|
|
5469
5662
|
};
|
|
5470
5663
|
}
|
|
5471
5664
|
function parseCodeAgentTraceRef(value) {
|
|
5472
|
-
if (!
|
|
5665
|
+
if (!isRecord6(value))
|
|
5473
5666
|
throw new Error("Code Agent trace ref must be an object.");
|
|
5474
5667
|
if (value.version !== 1)
|
|
5475
5668
|
throw new Error("Code Agent trace ref version must be 1.");
|
|
@@ -5508,8 +5701,8 @@ async function writeCodeAgentTraceRef(input) {
|
|
|
5508
5701
|
target: ref.target,
|
|
5509
5702
|
sessionKey: ref.sessionKey
|
|
5510
5703
|
});
|
|
5511
|
-
await
|
|
5512
|
-
await
|
|
5704
|
+
await mkdir5(dirname5(path), { recursive: true });
|
|
5705
|
+
await writeFile4(path, `${JSON.stringify(ref, null, 2)}
|
|
5513
5706
|
`, "utf8");
|
|
5514
5707
|
return { ref, path };
|
|
5515
5708
|
}
|
|
@@ -5756,7 +5949,7 @@ function stripTrailingSlash3(path) {
|
|
|
5756
5949
|
function isNotFoundError2(error) {
|
|
5757
5950
|
return error instanceof Error && (("code" in error) && error.code === "ENOENT" || error.message.includes("ENOENT"));
|
|
5758
5951
|
}
|
|
5759
|
-
function
|
|
5952
|
+
function isRecord6(value) {
|
|
5760
5953
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
5761
5954
|
}
|
|
5762
5955
|
// packages/core/src/config/errors.ts
|
|
@@ -5837,10 +6030,19 @@ import { readFile as readFile12 } from "node:fs/promises";
|
|
|
5837
6030
|
// packages/core/src/evolution/evidence/session-memory/index.ts
|
|
5838
6031
|
var exports_session_memory = {};
|
|
5839
6032
|
__export(exports_session_memory, {
|
|
6033
|
+
writeSessionIndex: () => writeSessionIndex,
|
|
6034
|
+
writeJson: () => writeJson2,
|
|
5840
6035
|
updateSessionMemoryFromHook: () => updateSessionMemoryFromHook,
|
|
5841
6036
|
resolveSessionMemoryPaths: () => resolveSessionMemoryPaths,
|
|
6037
|
+
readSessionState: () => readSessionState,
|
|
6038
|
+
readSessionEvidenceSegment: () => readSessionEvidenceSegment,
|
|
6039
|
+
readSessionCursor: () => readSessionCursor,
|
|
6040
|
+
readLineRange: () => readLineRange,
|
|
5842
6041
|
parseSessionMemoryPolicy: () => parseSessionMemoryPolicy,
|
|
5843
|
-
|
|
6042
|
+
listSessionEvidenceSegments: () => listSessionEvidenceSegments,
|
|
6043
|
+
createDefaultSessionMemoryPolicy: () => createDefaultSessionMemoryPolicy,
|
|
6044
|
+
appendRawEvent: () => appendRawEvent,
|
|
6045
|
+
SessionMemoryEvidenceError: () => SessionMemoryEvidenceError
|
|
5844
6046
|
});
|
|
5845
6047
|
|
|
5846
6048
|
// packages/core/src/evolution/evidence/session-memory/paths.ts
|
|
@@ -5890,8 +6092,208 @@ function parseSessionMemoryPolicy(value) {
|
|
|
5890
6092
|
toolCallsBetweenUpdates: positiveInteger(value.toolCallsBetweenUpdates, defaults.toolCallsBetweenUpdates)
|
|
5891
6093
|
};
|
|
5892
6094
|
}
|
|
6095
|
+
// packages/core/src/evolution/evidence/session-memory/storage.ts
|
|
6096
|
+
import { appendFile as appendFile2, mkdir as mkdir6, readFile as readFile7, readdir as readdir7, writeFile as writeFile5 } from "node:fs/promises";
|
|
6097
|
+
import { dirname as dirname6, join as join9 } from "node:path";
|
|
6098
|
+
|
|
6099
|
+
// packages/core/src/evolution/evidence/session-memory/constants.ts
|
|
6100
|
+
var DEFAULT_MAX_RAW_EVENT_BYTES = 64 * 1024;
|
|
6101
|
+
var SENSITIVE_TEXT_PATTERN2 = /https?:\/\/\S+|\b(secret|token|password|passwd|private|internal|api[_-]?key|apikey|credential|credentials|\.env)\b/i;
|
|
6102
|
+
var EXPLICIT_MEMORY_PATTERN = /\b(remember|memorize|do not repeat|don't repeat|this is important|save this|learn this)\b/i;
|
|
6103
|
+
var FAILURE_PATTERN = /\b(fail|failed|failure|error|issue|exception|crash)\b/i;
|
|
6104
|
+
var VERIFICATION_PATTERN = /\b(test|lint|typecheck|build|verify|verification|passed|success)\b/i;
|
|
6105
|
+
|
|
6106
|
+
// packages/core/src/evolution/evidence/session-memory/storage.ts
|
|
6107
|
+
class SessionMemoryEvidenceError extends Error {
|
|
6108
|
+
code;
|
|
6109
|
+
constructor(code, message) {
|
|
6110
|
+
super(message);
|
|
6111
|
+
this.name = "SessionMemoryEvidenceError";
|
|
6112
|
+
this.code = code;
|
|
6113
|
+
}
|
|
6114
|
+
}
|
|
6115
|
+
async function listSessionEvidenceSegments(input) {
|
|
6116
|
+
const rootDir = join9(resolveEvoDevPaths(input.homeDir).stateDir, "session-memory");
|
|
6117
|
+
const projectDirs = input.projectKey === undefined ? await listDirectoryNames2(rootDir) : [input.projectKey];
|
|
6118
|
+
const segments = [];
|
|
6119
|
+
for (const projectKey of projectDirs) {
|
|
6120
|
+
const paths = resolveSessionMemoryPaths({
|
|
6121
|
+
homeDir: input.homeDir,
|
|
6122
|
+
projectKey,
|
|
6123
|
+
sessionKey: "list"
|
|
6124
|
+
});
|
|
6125
|
+
for (const sessionKey of await listDirectoryNames2(paths.projectDir)) {
|
|
6126
|
+
const sessionPaths = resolveSessionMemoryPaths({
|
|
6127
|
+
homeDir: input.homeDir,
|
|
6128
|
+
projectKey,
|
|
6129
|
+
sessionKey
|
|
6130
|
+
});
|
|
6131
|
+
for (const entry of await listJsonFiles(sessionPaths.segmentsDir)) {
|
|
6132
|
+
try {
|
|
6133
|
+
segments.push(parseSessionEvidenceSegment(JSON.parse(await readFile7(entry, "utf8"))));
|
|
6134
|
+
} catch {}
|
|
6135
|
+
}
|
|
6136
|
+
}
|
|
6137
|
+
}
|
|
6138
|
+
return segments.sort((left, right) => right.createdAt.localeCompare(left.createdAt));
|
|
6139
|
+
}
|
|
6140
|
+
async function readSessionEvidenceSegment(input) {
|
|
6141
|
+
const paths = resolveSessionMemoryPaths(input);
|
|
6142
|
+
try {
|
|
6143
|
+
const segment = parseSessionEvidenceSegment(JSON.parse(await readFile7(paths.segmentPath(input.segmentId), "utf8")));
|
|
6144
|
+
if (segment.id !== input.segmentId || segment.projectKey !== input.projectKey || segment.sessionKey !== input.sessionKey) {
|
|
6145
|
+
throw new SessionMemoryEvidenceError("invalid", "Session evidence identity does not match its storage path.");
|
|
6146
|
+
}
|
|
6147
|
+
return segment;
|
|
6148
|
+
} catch (error) {
|
|
6149
|
+
if (error instanceof SessionMemoryEvidenceError)
|
|
6150
|
+
throw error;
|
|
6151
|
+
if (isNotFoundError(error)) {
|
|
6152
|
+
throw new SessionMemoryEvidenceError("not-found", "Session evidence segment was not found.");
|
|
6153
|
+
}
|
|
6154
|
+
throw new SessionMemoryEvidenceError("invalid", "Session evidence segment is invalid.");
|
|
6155
|
+
}
|
|
6156
|
+
}
|
|
6157
|
+
async function readSessionState(path) {
|
|
6158
|
+
try {
|
|
6159
|
+
return JSON.parse(await readFile7(path, "utf8"));
|
|
6160
|
+
} catch (error) {
|
|
6161
|
+
if (isNotFoundError(error))
|
|
6162
|
+
return null;
|
|
6163
|
+
throw error;
|
|
6164
|
+
}
|
|
6165
|
+
}
|
|
6166
|
+
async function readSessionCursor(path, sessionKey, sourcePath, now) {
|
|
6167
|
+
try {
|
|
6168
|
+
return JSON.parse(await readFile7(path, "utf8"));
|
|
6169
|
+
} catch (error) {
|
|
6170
|
+
if (!isNotFoundError(error))
|
|
6171
|
+
throw error;
|
|
6172
|
+
return {
|
|
6173
|
+
schemaVersion: 1,
|
|
6174
|
+
kind: "session-memory-cursor",
|
|
6175
|
+
sessionKey,
|
|
6176
|
+
traceRefId: null,
|
|
6177
|
+
sourcePath,
|
|
6178
|
+
lastCapturedOffset: null,
|
|
6179
|
+
lastCapturedLine: null,
|
|
6180
|
+
lastCapturedEventId: null,
|
|
6181
|
+
updatedAt: now
|
|
6182
|
+
};
|
|
6183
|
+
}
|
|
6184
|
+
}
|
|
6185
|
+
async function appendRawEvent(path, event) {
|
|
6186
|
+
const existingLineCount = await countJsonlLines(path);
|
|
6187
|
+
await mkdir6(dirname6(path), { recursive: true });
|
|
6188
|
+
await appendFile2(path, `${JSON.stringify(event)}
|
|
6189
|
+
`, "utf8");
|
|
6190
|
+
return { lineNumber: existingLineCount + 1 };
|
|
6191
|
+
}
|
|
6192
|
+
async function readLineRange(path, fromLine, toLine) {
|
|
6193
|
+
const text2 = await readFile7(path, "utf8");
|
|
6194
|
+
const lines = text2.split(`
|
|
6195
|
+
`).filter((line) => line.trim() !== "");
|
|
6196
|
+
const selected = lines.slice(Math.max(0, fromLine - 1), toLine);
|
|
6197
|
+
let secretsDetected = false;
|
|
6198
|
+
for (const line of selected) {
|
|
6199
|
+
try {
|
|
6200
|
+
const parsed = JSON.parse(line);
|
|
6201
|
+
secretsDetected = secretsDetected || parsed.secretsDetected === true;
|
|
6202
|
+
} catch {
|
|
6203
|
+
secretsDetected = secretsDetected || SENSITIVE_TEXT_PATTERN2.test(line);
|
|
6204
|
+
}
|
|
6205
|
+
}
|
|
6206
|
+
return { content: selected.join(`
|
|
6207
|
+
`), truncated: false, secretsDetected };
|
|
6208
|
+
}
|
|
6209
|
+
async function writeSessionIndex(paths, state, segment, now) {
|
|
6210
|
+
const existing = await readSessionIndex(paths.indexPath);
|
|
6211
|
+
const segments = [
|
|
6212
|
+
...existing.segments.filter((item) => item.id !== segment.id),
|
|
6213
|
+
{
|
|
6214
|
+
id: segment.id,
|
|
6215
|
+
reason: segment.reason,
|
|
6216
|
+
strength: segment.strength,
|
|
6217
|
+
createdAt: segment.createdAt,
|
|
6218
|
+
reviewState: segment.lifecycle.reviewState
|
|
6219
|
+
}
|
|
6220
|
+
];
|
|
6221
|
+
await writeJson2(paths.indexPath, {
|
|
6222
|
+
schemaVersion: 1,
|
|
6223
|
+
kind: "session-memory-index",
|
|
6224
|
+
projectKey: state.projectKey,
|
|
6225
|
+
sessionKey: state.sessionKey,
|
|
6226
|
+
updatedAt: now,
|
|
6227
|
+
lastSegmentId: segment.id,
|
|
6228
|
+
segments
|
|
6229
|
+
});
|
|
6230
|
+
}
|
|
6231
|
+
async function writeJson2(path, value) {
|
|
6232
|
+
await mkdir6(dirname6(path), { recursive: true });
|
|
6233
|
+
await writeFile5(path, `${JSON.stringify(value, null, 2)}
|
|
6234
|
+
`, "utf8");
|
|
6235
|
+
}
|
|
6236
|
+
async function countJsonlLines(path) {
|
|
6237
|
+
try {
|
|
6238
|
+
const text2 = await readFile7(path, "utf8");
|
|
6239
|
+
return text2.split(`
|
|
6240
|
+
`).filter((line) => line.trim() !== "").length;
|
|
6241
|
+
} catch (error) {
|
|
6242
|
+
if (isNotFoundError(error))
|
|
6243
|
+
return 0;
|
|
6244
|
+
throw error;
|
|
6245
|
+
}
|
|
6246
|
+
}
|
|
6247
|
+
async function readSessionIndex(path) {
|
|
6248
|
+
try {
|
|
6249
|
+
const parsed = JSON.parse(await readFile7(path, "utf8"));
|
|
6250
|
+
if (!Array.isArray(parsed.segments))
|
|
6251
|
+
return { segments: [] };
|
|
6252
|
+
return {
|
|
6253
|
+
segments: parsed.segments.filter(isSessionIndexSegment)
|
|
6254
|
+
};
|
|
6255
|
+
} catch (error) {
|
|
6256
|
+
if (isNotFoundError(error))
|
|
6257
|
+
return { segments: [] };
|
|
6258
|
+
throw error;
|
|
6259
|
+
}
|
|
6260
|
+
}
|
|
6261
|
+
function isSessionIndexSegment(value) {
|
|
6262
|
+
if (typeof value !== "object" || value === null || Array.isArray(value))
|
|
6263
|
+
return false;
|
|
6264
|
+
const item = value;
|
|
6265
|
+
return typeof item.id === "string" && typeof item.reason === "string" && typeof item.strength === "string" && typeof item.createdAt === "string" && typeof item.reviewState === "string";
|
|
6266
|
+
}
|
|
6267
|
+
function parseSessionEvidenceSegment(value) {
|
|
6268
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
6269
|
+
throw new SessionMemoryEvidenceError("invalid", "Session evidence must be an object.");
|
|
6270
|
+
}
|
|
6271
|
+
const segment = value;
|
|
6272
|
+
if (segment.schemaVersion !== 1 || segment.kind !== "session-evidence-segment" || typeof segment.id !== "string" || typeof segment.projectKey !== "string" || typeof segment.sessionKey !== "string" || typeof segment.createdAt !== "string" || typeof segment.normalized !== "object" || segment.normalized === null || typeof segment.rawExcerpt !== "object" || segment.rawExcerpt === null) {
|
|
6273
|
+
throw new SessionMemoryEvidenceError("invalid", "Session evidence fields are invalid.");
|
|
6274
|
+
}
|
|
6275
|
+
return segment;
|
|
6276
|
+
}
|
|
6277
|
+
async function listDirectoryNames2(path) {
|
|
6278
|
+
try {
|
|
6279
|
+
return (await readdir7(path, { withFileTypes: true })).filter((entry) => entry.isDirectory()).map((entry) => entry.name).sort();
|
|
6280
|
+
} catch (error) {
|
|
6281
|
+
if (isNotFoundError(error))
|
|
6282
|
+
return [];
|
|
6283
|
+
throw error;
|
|
6284
|
+
}
|
|
6285
|
+
}
|
|
6286
|
+
async function listJsonFiles(path) {
|
|
6287
|
+
try {
|
|
6288
|
+
return (await readdir7(path, { withFileTypes: true })).filter((entry) => entry.isFile() && entry.name.endsWith(".json")).map((entry) => join9(path, entry.name)).sort();
|
|
6289
|
+
} catch (error) {
|
|
6290
|
+
if (isNotFoundError(error))
|
|
6291
|
+
return [];
|
|
6292
|
+
throw error;
|
|
6293
|
+
}
|
|
6294
|
+
}
|
|
5893
6295
|
// packages/core/src/evolution/evidence/session-memory/updater.ts
|
|
5894
|
-
import { join as
|
|
6296
|
+
import { join as join11 } from "node:path";
|
|
5895
6297
|
|
|
5896
6298
|
// packages/core/src/evolution/triggers/index.ts
|
|
5897
6299
|
var exports_triggers = {};
|
|
@@ -5906,8 +6308,8 @@ __export(exports_triggers, {
|
|
|
5906
6308
|
enqueueSegmentEvolutionTrigger: () => enqueueSegmentEvolutionTrigger,
|
|
5907
6309
|
enqueueEvolutionTrigger: () => enqueueEvolutionTrigger
|
|
5908
6310
|
});
|
|
5909
|
-
import { readFile as
|
|
5910
|
-
import { join as
|
|
6311
|
+
import { readFile as readFile8 } from "node:fs/promises";
|
|
6312
|
+
import { join as join10 } from "node:path";
|
|
5911
6313
|
|
|
5912
6314
|
// packages/core/src/evolution/triggers/classification.ts
|
|
5913
6315
|
function normalizeEvolutionEventType(value) {
|
|
@@ -6042,19 +6444,19 @@ async function enqueueEvolutionTrigger(input) {
|
|
|
6042
6444
|
};
|
|
6043
6445
|
validateEvolutionTriggerRecord(trigger);
|
|
6044
6446
|
const paths = resolveEvolutionPaths({ homeDir: input.homeDir, projectKey, runId });
|
|
6045
|
-
await writeJsonFile(
|
|
6447
|
+
await writeJsonFile(join10(paths.triggersDir, `${trigger.id}.json`), trigger, { overwrite: false });
|
|
6046
6448
|
return trigger;
|
|
6047
6449
|
}
|
|
6048
6450
|
async function listEvolutionTriggers(input) {
|
|
6049
6451
|
const paths = resolveEvoDevPaths(input.homeDir);
|
|
6050
|
-
const evolutionStateDir =
|
|
6452
|
+
const evolutionStateDir = join10(paths.stateDir, "evolution");
|
|
6051
6453
|
const projectKeys = input.projectKey === undefined ? await listDirectoryNames(evolutionStateDir) : [sanitizeStorageId2("projectKey", input.projectKey)];
|
|
6052
6454
|
const triggers = [];
|
|
6053
6455
|
for (const projectKey of projectKeys) {
|
|
6054
|
-
const projectStateDir =
|
|
6456
|
+
const projectStateDir = join10(evolutionStateDir, projectKey);
|
|
6055
6457
|
const runIds = input.runId === undefined ? await listDirectoryNames(projectStateDir) : [sanitizeStorageId2("runId", input.runId)];
|
|
6056
6458
|
for (const runId of runIds) {
|
|
6057
|
-
const runTriggers = await readJsonFiles(
|
|
6459
|
+
const runTriggers = await readJsonFiles(join10(projectStateDir, runId, "triggers"), parseTrigger);
|
|
6058
6460
|
triggers.push(...runTriggers.filter((trigger) => input.status === undefined || trigger.status === input.status));
|
|
6059
6461
|
}
|
|
6060
6462
|
}
|
|
@@ -6095,16 +6497,16 @@ async function enqueueSegmentEvolutionTrigger(input) {
|
|
|
6095
6497
|
} catch (error) {
|
|
6096
6498
|
if (!isFileExistsError(error))
|
|
6097
6499
|
throw error;
|
|
6098
|
-
return parseSegmentTrigger(JSON.parse(await
|
|
6500
|
+
return parseSegmentTrigger(JSON.parse(await readFile8(path, "utf8")));
|
|
6099
6501
|
}
|
|
6100
6502
|
}
|
|
6101
6503
|
async function listSegmentEvolutionTriggers(input) {
|
|
6102
6504
|
const paths = resolveEvoDevPaths(input.homeDir);
|
|
6103
|
-
const evolutionStateDir =
|
|
6505
|
+
const evolutionStateDir = join10(paths.stateDir, "evolution");
|
|
6104
6506
|
const projectKeys = input.projectKey === undefined ? await listDirectoryNames(evolutionStateDir) : [sanitizeStorageId2("projectKey", input.projectKey)];
|
|
6105
6507
|
const triggers = [];
|
|
6106
6508
|
for (const projectKey of projectKeys) {
|
|
6107
|
-
const projectSegmentsDir =
|
|
6509
|
+
const projectSegmentsDir = join10(evolutionStateDir, projectKey, "segments");
|
|
6108
6510
|
const projectTriggers = await readJsonFiles(projectSegmentsDir, parseSegmentTrigger);
|
|
6109
6511
|
triggers.push(...projectTriggers.filter((trigger) => {
|
|
6110
6512
|
if (input.status !== undefined && trigger.status !== input.status)
|
|
@@ -6141,7 +6543,7 @@ async function updateTriggers(homeDir, triggers, patch) {
|
|
|
6141
6543
|
projectKey: next.projectKey,
|
|
6142
6544
|
runId: next.runId
|
|
6143
6545
|
});
|
|
6144
|
-
await writeJsonFile(
|
|
6546
|
+
await writeJsonFile(join10(paths.triggersDir, `${next.id}.json`), next, { overwrite: true });
|
|
6145
6547
|
}
|
|
6146
6548
|
}
|
|
6147
6549
|
async function updateSegmentTriggers(homeDir, triggers, patch) {
|
|
@@ -6160,128 +6562,7 @@ async function updateSegmentTriggers(homeDir, triggers, patch) {
|
|
|
6160
6562
|
}
|
|
6161
6563
|
function resolveSegmentEvolutionTriggerPath(homeDir, trigger) {
|
|
6162
6564
|
const paths = resolveEvoDevPaths(homeDir);
|
|
6163
|
-
return
|
|
6164
|
-
}
|
|
6165
|
-
|
|
6166
|
-
// packages/core/src/evolution/evidence/session-memory/constants.ts
|
|
6167
|
-
var DEFAULT_MAX_RAW_EVENT_BYTES = 64 * 1024;
|
|
6168
|
-
var SENSITIVE_TEXT_PATTERN2 = /https?:\/\/\S+|\b(secret|token|password|passwd|private|internal|api[_-]?key|apikey|credential|credentials|\.env)\b/i;
|
|
6169
|
-
var EXPLICIT_MEMORY_PATTERN = /\b(remember|memorize|do not repeat|don't repeat|this is important|save this|learn this)\b/i;
|
|
6170
|
-
var FAILURE_PATTERN = /\b(fail|failed|failure|error|issue|exception|crash)\b/i;
|
|
6171
|
-
var VERIFICATION_PATTERN = /\b(test|lint|typecheck|build|verify|verification|passed|success)\b/i;
|
|
6172
|
-
|
|
6173
|
-
// packages/core/src/evolution/evidence/session-memory/storage.ts
|
|
6174
|
-
import { appendFile as appendFile2, mkdir as mkdir5, readFile as readFile8, writeFile as writeFile4 } from "node:fs/promises";
|
|
6175
|
-
import { dirname as dirname5 } from "node:path";
|
|
6176
|
-
async function readSessionState(path) {
|
|
6177
|
-
try {
|
|
6178
|
-
return JSON.parse(await readFile8(path, "utf8"));
|
|
6179
|
-
} catch (error) {
|
|
6180
|
-
if (isNotFoundError(error))
|
|
6181
|
-
return null;
|
|
6182
|
-
throw error;
|
|
6183
|
-
}
|
|
6184
|
-
}
|
|
6185
|
-
async function readSessionCursor(path, sessionKey, sourcePath, now) {
|
|
6186
|
-
try {
|
|
6187
|
-
return JSON.parse(await readFile8(path, "utf8"));
|
|
6188
|
-
} catch (error) {
|
|
6189
|
-
if (!isNotFoundError(error))
|
|
6190
|
-
throw error;
|
|
6191
|
-
return {
|
|
6192
|
-
schemaVersion: 1,
|
|
6193
|
-
kind: "session-memory-cursor",
|
|
6194
|
-
sessionKey,
|
|
6195
|
-
traceRefId: null,
|
|
6196
|
-
sourcePath,
|
|
6197
|
-
lastCapturedOffset: null,
|
|
6198
|
-
lastCapturedLine: null,
|
|
6199
|
-
lastCapturedEventId: null,
|
|
6200
|
-
updatedAt: now
|
|
6201
|
-
};
|
|
6202
|
-
}
|
|
6203
|
-
}
|
|
6204
|
-
async function appendRawEvent(path, event) {
|
|
6205
|
-
const existingLineCount = await countJsonlLines(path);
|
|
6206
|
-
await mkdir5(dirname5(path), { recursive: true });
|
|
6207
|
-
await appendFile2(path, `${JSON.stringify(event)}
|
|
6208
|
-
`, "utf8");
|
|
6209
|
-
return { lineNumber: existingLineCount + 1 };
|
|
6210
|
-
}
|
|
6211
|
-
async function readLineRange(path, fromLine, toLine) {
|
|
6212
|
-
const text2 = await readFile8(path, "utf8");
|
|
6213
|
-
const lines = text2.split(`
|
|
6214
|
-
`).filter((line) => line.trim() !== "");
|
|
6215
|
-
const selected = lines.slice(Math.max(0, fromLine - 1), toLine);
|
|
6216
|
-
let secretsDetected = false;
|
|
6217
|
-
for (const line of selected) {
|
|
6218
|
-
try {
|
|
6219
|
-
const parsed = JSON.parse(line);
|
|
6220
|
-
secretsDetected = secretsDetected || parsed.secretsDetected === true;
|
|
6221
|
-
} catch {
|
|
6222
|
-
secretsDetected = secretsDetected || SENSITIVE_TEXT_PATTERN2.test(line);
|
|
6223
|
-
}
|
|
6224
|
-
}
|
|
6225
|
-
return { content: selected.join(`
|
|
6226
|
-
`), truncated: false, secretsDetected };
|
|
6227
|
-
}
|
|
6228
|
-
async function writeSessionIndex(paths, state, segment, now) {
|
|
6229
|
-
const existing = await readSessionIndex(paths.indexPath);
|
|
6230
|
-
const segments = [
|
|
6231
|
-
...existing.segments.filter((item) => item.id !== segment.id),
|
|
6232
|
-
{
|
|
6233
|
-
id: segment.id,
|
|
6234
|
-
reason: segment.reason,
|
|
6235
|
-
strength: segment.strength,
|
|
6236
|
-
createdAt: segment.createdAt,
|
|
6237
|
-
reviewState: segment.lifecycle.reviewState
|
|
6238
|
-
}
|
|
6239
|
-
];
|
|
6240
|
-
await writeJson2(paths.indexPath, {
|
|
6241
|
-
schemaVersion: 1,
|
|
6242
|
-
kind: "session-memory-index",
|
|
6243
|
-
projectKey: state.projectKey,
|
|
6244
|
-
sessionKey: state.sessionKey,
|
|
6245
|
-
updatedAt: now,
|
|
6246
|
-
lastSegmentId: segment.id,
|
|
6247
|
-
segments
|
|
6248
|
-
});
|
|
6249
|
-
}
|
|
6250
|
-
async function writeJson2(path, value) {
|
|
6251
|
-
await mkdir5(dirname5(path), { recursive: true });
|
|
6252
|
-
await writeFile4(path, `${JSON.stringify(value, null, 2)}
|
|
6253
|
-
`, "utf8");
|
|
6254
|
-
}
|
|
6255
|
-
async function countJsonlLines(path) {
|
|
6256
|
-
try {
|
|
6257
|
-
const text2 = await readFile8(path, "utf8");
|
|
6258
|
-
return text2.split(`
|
|
6259
|
-
`).filter((line) => line.trim() !== "").length;
|
|
6260
|
-
} catch (error) {
|
|
6261
|
-
if (isNotFoundError(error))
|
|
6262
|
-
return 0;
|
|
6263
|
-
throw error;
|
|
6264
|
-
}
|
|
6265
|
-
}
|
|
6266
|
-
async function readSessionIndex(path) {
|
|
6267
|
-
try {
|
|
6268
|
-
const parsed = JSON.parse(await readFile8(path, "utf8"));
|
|
6269
|
-
if (!Array.isArray(parsed.segments))
|
|
6270
|
-
return { segments: [] };
|
|
6271
|
-
return {
|
|
6272
|
-
segments: parsed.segments.filter(isSessionIndexSegment)
|
|
6273
|
-
};
|
|
6274
|
-
} catch (error) {
|
|
6275
|
-
if (isNotFoundError(error))
|
|
6276
|
-
return { segments: [] };
|
|
6277
|
-
throw error;
|
|
6278
|
-
}
|
|
6279
|
-
}
|
|
6280
|
-
function isSessionIndexSegment(value) {
|
|
6281
|
-
if (typeof value !== "object" || value === null || Array.isArray(value))
|
|
6282
|
-
return false;
|
|
6283
|
-
const item = value;
|
|
6284
|
-
return typeof item.id === "string" && typeof item.reason === "string" && typeof item.strength === "string" && typeof item.createdAt === "string" && typeof item.reviewState === "string";
|
|
6565
|
+
return join10(paths.stateDir, "evolution", sanitizeStorageId2("projectKey", trigger.projectKey), "segments", `${sanitizeStorageId2("segmentTrigger", trigger.id)}.json`);
|
|
6285
6566
|
}
|
|
6286
6567
|
|
|
6287
6568
|
// packages/core/src/evolution/evidence/session-memory/segment.ts
|
|
@@ -6379,8 +6660,8 @@ async function createSegment(input) {
|
|
|
6379
6660
|
externalUploadAllowed: false
|
|
6380
6661
|
},
|
|
6381
6662
|
lifecycle: {
|
|
6382
|
-
status: "
|
|
6383
|
-
reviewState: "
|
|
6663
|
+
status: "captured",
|
|
6664
|
+
reviewState: "not-required",
|
|
6384
6665
|
consumedByBatchIds: []
|
|
6385
6666
|
}
|
|
6386
6667
|
};
|
|
@@ -6690,7 +6971,7 @@ async function updateSessionMemoryFromHook(input) {
|
|
|
6690
6971
|
await writeJson2(paths.cursorPath, cursor);
|
|
6691
6972
|
stateWrites.push(paths.statePath, paths.cursorPath);
|
|
6692
6973
|
if (queuedSegmentTriggerId !== null) {
|
|
6693
|
-
stateWrites.push(
|
|
6974
|
+
stateWrites.push(join11(input.homeDir, ".evodev", "state", "evolution", projectKey, "segments", `${queuedSegmentTriggerId}.json`));
|
|
6694
6975
|
}
|
|
6695
6976
|
return {
|
|
6696
6977
|
state,
|
|
@@ -6712,12 +6993,12 @@ function emptyResult() {
|
|
|
6712
6993
|
};
|
|
6713
6994
|
}
|
|
6714
6995
|
// packages/core/src/hooks/index.ts
|
|
6715
|
-
import { mkdir as
|
|
6716
|
-
import { dirname as
|
|
6996
|
+
import { mkdir as mkdir9, readFile as readFile11, writeFile as writeFile8 } from "node:fs/promises";
|
|
6997
|
+
import { dirname as dirname9, join as join14 } from "node:path";
|
|
6717
6998
|
|
|
6718
6999
|
// packages/core/src/task/index.ts
|
|
6719
|
-
import { lstat, mkdir as
|
|
6720
|
-
import { basename, dirname as
|
|
7000
|
+
import { lstat, mkdir as mkdir7, readFile as readFile9, realpath, stat as stat5, writeFile as writeFile6 } from "node:fs/promises";
|
|
7001
|
+
import { basename, dirname as dirname7, join as join12, resolve as resolve3 } from "node:path";
|
|
6721
7002
|
var FORBIDDEN_TASK_PATHS = ["CLAUDE.md", "AGENTS.md", ".claude/**", ".codex/**"];
|
|
6722
7003
|
var FORBIDDEN_TASK_WRITE_SEGMENTS = new Set([".claude", ".codex"]);
|
|
6723
7004
|
var FORBIDDEN_PROJECT_ASSET_SEGMENTS = new Set(["packages", "src"]);
|
|
@@ -6917,8 +7198,8 @@ async function readTaskContract(path) {
|
|
|
6917
7198
|
}
|
|
6918
7199
|
async function writeTaskContract(path, contract, options = {}) {
|
|
6919
7200
|
await assertTaskContractWritePathAllowed(path);
|
|
6920
|
-
await
|
|
6921
|
-
await
|
|
7201
|
+
await mkdir7(dirname7(path), { recursive: true });
|
|
7202
|
+
await writeFile6(path, `${JSON.stringify(contract, null, 2)}
|
|
6922
7203
|
`, {
|
|
6923
7204
|
encoding: "utf8",
|
|
6924
7205
|
flag: options.overwrite === true ? "w" : "wx"
|
|
@@ -6926,16 +7207,16 @@ async function writeTaskContract(path, contract, options = {}) {
|
|
|
6926
7207
|
}
|
|
6927
7208
|
async function resolveTaskContractOutputPath(input) {
|
|
6928
7209
|
if (input.outputDir !== undefined) {
|
|
6929
|
-
const outputPath =
|
|
7210
|
+
const outputPath = join12(input.outputDir, input.taskId, "contract.json");
|
|
6930
7211
|
await assertTaskContractWritePathAllowed(outputPath);
|
|
6931
7212
|
return outputPath;
|
|
6932
7213
|
}
|
|
6933
7214
|
if (input.projectDir !== undefined) {
|
|
6934
|
-
const projectContextPath =
|
|
7215
|
+
const projectContextPath = join12(input.projectDir, ".evodev", "project.json");
|
|
6935
7216
|
if (!await pathExists4(projectContextPath)) {
|
|
6936
7217
|
throw new Error("Project mode requires existing .evodev/project.json; use --output-dir instead.");
|
|
6937
7218
|
}
|
|
6938
|
-
const outputPath =
|
|
7219
|
+
const outputPath = join12(input.projectDir, ".evodev", "tasks", input.taskId, "contract.json");
|
|
6939
7220
|
await assertTaskContractWritePathAllowed(outputPath);
|
|
6940
7221
|
return outputPath;
|
|
6941
7222
|
}
|
|
@@ -7140,7 +7421,7 @@ function isVerificationReady(contract) {
|
|
|
7140
7421
|
return contract.status !== "draft" && contract.route.mode !== null && contract.route.workflowId !== null && (contract.route.requiredVerification ?? []).length > 0;
|
|
7141
7422
|
}
|
|
7142
7423
|
function parseTaskContract(value) {
|
|
7143
|
-
if (!
|
|
7424
|
+
if (!isRecord7(value) || value.version !== 1 || typeof value.taskId !== "string") {
|
|
7144
7425
|
throw new Error("Invalid Task Contract JSON.");
|
|
7145
7426
|
}
|
|
7146
7427
|
return value;
|
|
@@ -7151,7 +7432,7 @@ function assertMetadataOnly(value) {
|
|
|
7151
7432
|
assertMetadataOnly(item);
|
|
7152
7433
|
return;
|
|
7153
7434
|
}
|
|
7154
|
-
if (!
|
|
7435
|
+
if (!isRecord7(value)) {
|
|
7155
7436
|
if (typeof value === "string" && containsSensitiveText(value)) {
|
|
7156
7437
|
throw new Error("Verification input contains sensitive content in metadata field.");
|
|
7157
7438
|
}
|
|
@@ -7229,11 +7510,11 @@ async function resolveTaskWriteRealPath(path) {
|
|
|
7229
7510
|
while (true) {
|
|
7230
7511
|
try {
|
|
7231
7512
|
await lstat(currentPath);
|
|
7232
|
-
return
|
|
7513
|
+
return join12(await realpath(currentPath), ...missingSegments.reverse());
|
|
7233
7514
|
} catch (error) {
|
|
7234
7515
|
if (!isMissingPathError(error))
|
|
7235
7516
|
throw error;
|
|
7236
|
-
const parentPath =
|
|
7517
|
+
const parentPath = dirname7(currentPath);
|
|
7237
7518
|
if (parentPath === currentPath)
|
|
7238
7519
|
return resolve3(path);
|
|
7239
7520
|
missingSegments.push(basename(currentPath));
|
|
@@ -7275,14 +7556,14 @@ async function pathExists4(path) {
|
|
|
7275
7556
|
function isMissingPathError(error) {
|
|
7276
7557
|
return error instanceof Error && "code" in error && (error.code === "ENOENT" || error.code === "ENOTDIR");
|
|
7277
7558
|
}
|
|
7278
|
-
function
|
|
7559
|
+
function isRecord7(value) {
|
|
7279
7560
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
7280
7561
|
}
|
|
7281
7562
|
|
|
7282
7563
|
// packages/core/src/team/index.ts
|
|
7283
7564
|
import { spawn } from "node:child_process";
|
|
7284
|
-
import { appendFile as appendFile3, cp, mkdir as
|
|
7285
|
-
import { basename as basename2, dirname as
|
|
7565
|
+
import { appendFile as appendFile3, cp, mkdir as mkdir8, readFile as readFile10, readdir as readdir8, stat as stat6, writeFile as writeFile7 } from "node:fs/promises";
|
|
7566
|
+
import { basename as basename2, dirname as dirname8, extname, isAbsolute as isAbsolute5, join as join13, relative as relative6, resolve as resolve4 } from "node:path";
|
|
7286
7567
|
|
|
7287
7568
|
// packages/core/src/team/prompts.ts
|
|
7288
7569
|
var TEAM_ROLE_STARTUP_PROMPT_TEMPLATE = [
|
|
@@ -7483,8 +7764,8 @@ function createTeamRunStore(homeDir) {
|
|
|
7483
7764
|
paths: paths2,
|
|
7484
7765
|
async createRunDirs(runId) {
|
|
7485
7766
|
await migrateLegacyRunDirIfNeeded(paths2, runId);
|
|
7486
|
-
await
|
|
7487
|
-
await
|
|
7767
|
+
await mkdir8(paths2.runDir(runId), { recursive: true });
|
|
7768
|
+
await mkdir8(paths2.agentsDir(runId), { recursive: true });
|
|
7488
7769
|
},
|
|
7489
7770
|
async writeRun(run) {
|
|
7490
7771
|
await this.createRunDirs(run.runId);
|
|
@@ -7495,7 +7776,7 @@ function createTeamRunStore(homeDir) {
|
|
|
7495
7776
|
return parseTeamRunRecord(JSON.parse(await readFile10(paths2.runPath(runId), "utf8")));
|
|
7496
7777
|
},
|
|
7497
7778
|
async writeLatestRunId(runId) {
|
|
7498
|
-
await
|
|
7779
|
+
await mkdir8(paths2.runsDir, { recursive: true });
|
|
7499
7780
|
await writeJson3(paths2.latestRunPath, { version: 1, runId });
|
|
7500
7781
|
},
|
|
7501
7782
|
async readLatestRunId() {
|
|
@@ -7529,8 +7810,8 @@ function createTeamRunStore(homeDir) {
|
|
|
7529
7810
|
async readAgents(runId) {
|
|
7530
7811
|
await migrateLegacyRunDirIfNeeded(paths2, runId);
|
|
7531
7812
|
try {
|
|
7532
|
-
const entries = await
|
|
7533
|
-
const agents = await Promise.all(entries.filter((entry) => entry.endsWith(".json")).map((entry) => readFile10(
|
|
7813
|
+
const entries = await readdir8(paths2.agentsDir(runId));
|
|
7814
|
+
const agents = await Promise.all(entries.filter((entry) => entry.endsWith(".json")).map((entry) => readFile10(join13(paths2.agentsDir(runId), entry), "utf8").then((raw) => parseTeamAgentRecord(JSON.parse(raw)))));
|
|
7534
7815
|
return agents.sort((left, right) => left.roleId.localeCompare(right.roleId));
|
|
7535
7816
|
} catch {
|
|
7536
7817
|
return [];
|
|
@@ -7585,7 +7866,7 @@ function createTeamRunStore(homeDir) {
|
|
|
7585
7866
|
}
|
|
7586
7867
|
function resolveTeamRunPaths(homeDir) {
|
|
7587
7868
|
const paths2 = resolveEvoDevPaths(homeDir);
|
|
7588
|
-
const legacyRunsDir =
|
|
7869
|
+
const legacyRunsDir = join13(paths2.rootDir, "runs");
|
|
7589
7870
|
return {
|
|
7590
7871
|
rootDir: paths2.rootDir,
|
|
7591
7872
|
roleAgentsDir: paths2.roleAgentsDir,
|
|
@@ -7593,23 +7874,23 @@ function resolveTeamRunPaths(homeDir) {
|
|
|
7593
7874
|
runsDir: paths2.runsDir,
|
|
7594
7875
|
legacyRunsDir,
|
|
7595
7876
|
latestRunPath: paths2.latestRunPath,
|
|
7596
|
-
legacyLatestRunPath:
|
|
7597
|
-
runDir: (runId) =>
|
|
7598
|
-
legacyRunDir: (runId) =>
|
|
7599
|
-
runPath: (runId) =>
|
|
7600
|
-
legacyRunPath: (runId) =>
|
|
7601
|
-
tmuxPath: (runId) =>
|
|
7602
|
-
legacyTmuxPath: (runId) =>
|
|
7603
|
-
agentsDir: (runId) =>
|
|
7604
|
-
legacyAgentsDir: (runId) =>
|
|
7605
|
-
agentPath: (runId, roleId) =>
|
|
7606
|
-
legacyAgentPath: (runId, roleId) =>
|
|
7607
|
-
messagesPath: (runId) =>
|
|
7608
|
-
legacyMessagesPath: (runId) =>
|
|
7609
|
-
eventsPath: (runId) =>
|
|
7610
|
-
legacyEventsPath: (runId) =>
|
|
7611
|
-
statusPath: (runId) =>
|
|
7612
|
-
messageListPath: (runId) =>
|
|
7877
|
+
legacyLatestRunPath: join13(legacyRunsDir, "latest.json"),
|
|
7878
|
+
runDir: (runId) => join13(paths2.runsDir, runId),
|
|
7879
|
+
legacyRunDir: (runId) => join13(legacyRunsDir, runId),
|
|
7880
|
+
runPath: (runId) => join13(paths2.runsDir, runId, "run.json"),
|
|
7881
|
+
legacyRunPath: (runId) => join13(legacyRunsDir, runId, "run.json"),
|
|
7882
|
+
tmuxPath: (runId) => join13(paths2.runsDir, runId, "tmux.json"),
|
|
7883
|
+
legacyTmuxPath: (runId) => join13(legacyRunsDir, runId, "tmux.json"),
|
|
7884
|
+
agentsDir: (runId) => join13(paths2.runsDir, runId, "agents"),
|
|
7885
|
+
legacyAgentsDir: (runId) => join13(legacyRunsDir, runId, "agents"),
|
|
7886
|
+
agentPath: (runId, roleId) => join13(paths2.runsDir, runId, "agents", `${roleId}.json`),
|
|
7887
|
+
legacyAgentPath: (runId, roleId) => join13(legacyRunsDir, runId, "agents", `${roleId}.json`),
|
|
7888
|
+
messagesPath: (runId) => join13(paths2.runsDir, runId, "messages.jsonl"),
|
|
7889
|
+
legacyMessagesPath: (runId) => join13(legacyRunsDir, runId, "messages.jsonl"),
|
|
7890
|
+
eventsPath: (runId) => join13(paths2.runsDir, runId, "events.jsonl"),
|
|
7891
|
+
legacyEventsPath: (runId) => join13(legacyRunsDir, runId, "events.jsonl"),
|
|
7892
|
+
statusPath: (runId) => join13(paths2.runsDir, runId, "status.json"),
|
|
7893
|
+
messageListPath: (runId) => join13(paths2.runsDir, runId, "message-list.json")
|
|
7613
7894
|
};
|
|
7614
7895
|
}
|
|
7615
7896
|
async function migrateLegacyRunDirIfNeeded(paths2, runId) {
|
|
@@ -7619,7 +7900,7 @@ async function migrateLegacyRunDirIfNeeded(paths2, runId) {
|
|
|
7619
7900
|
const legacyDir = paths2.legacyRunDir(runId);
|
|
7620
7901
|
if (!await pathExists5(legacyDir))
|
|
7621
7902
|
return;
|
|
7622
|
-
await
|
|
7903
|
+
await mkdir8(dirname8(nextDir), { recursive: true });
|
|
7623
7904
|
await cp(legacyDir, nextDir, { recursive: true, errorOnExist: false, force: false });
|
|
7624
7905
|
}
|
|
7625
7906
|
async function writeTeamStatusSnapshot(input) {
|
|
@@ -8149,7 +8430,7 @@ async function listTeamRuns(input = {}) {
|
|
|
8149
8430
|
const entries = new Set;
|
|
8150
8431
|
for (const runsDir of [store.paths.runsDir, store.paths.legacyRunsDir]) {
|
|
8151
8432
|
try {
|
|
8152
|
-
for (const entry of await
|
|
8433
|
+
for (const entry of await readdir8(runsDir))
|
|
8153
8434
|
entries.add(entry);
|
|
8154
8435
|
} catch (error) {
|
|
8155
8436
|
if (!isNotFoundError3(error))
|
|
@@ -8481,7 +8762,7 @@ async function listTeamAgents(input = {}) {
|
|
|
8481
8762
|
}));
|
|
8482
8763
|
}
|
|
8483
8764
|
async function resolveTeamOverlay(input) {
|
|
8484
|
-
const repoTeamPath =
|
|
8765
|
+
const repoTeamPath = join13(input.repoRoot, ".evodev", "team", "team.md");
|
|
8485
8766
|
if (await pathExists5(repoTeamPath)) {
|
|
8486
8767
|
return {
|
|
8487
8768
|
source: "repo",
|
|
@@ -8508,7 +8789,7 @@ async function ensureDefaultTeamOverlay(input) {
|
|
|
8508
8789
|
const paths2 = resolveEvoDevPaths(input.homeDir);
|
|
8509
8790
|
const files = [];
|
|
8510
8791
|
for (const asset of assets) {
|
|
8511
|
-
const targetPath = asset.kind === "team" ?
|
|
8792
|
+
const targetPath = asset.kind === "team" ? join13(paths2.rootDir, "team", "team.md") : join13(paths2.rootDir, "team", "agents", asset.name);
|
|
8512
8793
|
const content = await readFile10(asset.sourcePath, "utf8");
|
|
8513
8794
|
files.push({
|
|
8514
8795
|
sourcePath: asset.sourcePath,
|
|
@@ -8542,7 +8823,7 @@ function resolveTeamAgentReference(input) {
|
|
|
8542
8823
|
return {
|
|
8543
8824
|
roleId: input.roleId,
|
|
8544
8825
|
reference,
|
|
8545
|
-
sourcePath:
|
|
8826
|
+
sourcePath: join13(resolveEvoDevPaths(input.homeDir).rootDir, "team", "agents", `${name}.md`),
|
|
8546
8827
|
scope: "global"
|
|
8547
8828
|
};
|
|
8548
8829
|
}
|
|
@@ -8579,7 +8860,7 @@ async function readTeamAgentDefinition(input) {
|
|
|
8579
8860
|
const reference = resolveTeamAgentReference(input);
|
|
8580
8861
|
const markdown = await readTeamAgentMarkdown(reference);
|
|
8581
8862
|
const parsed = parseMarkdownWithFrontmatter(markdown);
|
|
8582
|
-
const evodev =
|
|
8863
|
+
const evodev = isRecord8(parsed.frontmatter.evodev) ? parsed.frontmatter.evodev : {};
|
|
8583
8864
|
return {
|
|
8584
8865
|
roleId: input.roleId,
|
|
8585
8866
|
name: optionalString5(parsed.frontmatter.name) ?? defaultRoleName(input.roleId),
|
|
@@ -8662,7 +8943,7 @@ async function resolveTeamRole(input) {
|
|
|
8662
8943
|
nativeAgent: null
|
|
8663
8944
|
};
|
|
8664
8945
|
}
|
|
8665
|
-
const globalRolePath =
|
|
8946
|
+
const globalRolePath = join13(resolveEvoDevPaths(input.homeDir).roleAgentsDir, `${input.roleId}.json`);
|
|
8666
8947
|
const candidate = await readRoleCandidate(globalRolePath, "global");
|
|
8667
8948
|
const nativeAgent = await resolveTeamRoleNativeAgentBinding({
|
|
8668
8949
|
homeDir: input.homeDir,
|
|
@@ -8696,7 +8977,7 @@ async function resolveTeamRole(input) {
|
|
|
8696
8977
|
};
|
|
8697
8978
|
}
|
|
8698
8979
|
function parseTeamRoleDefinition(value, defaults) {
|
|
8699
|
-
const input =
|
|
8980
|
+
const input = isRecord8(value) ? value : {};
|
|
8700
8981
|
const roleId = optionalString5(input.roleId) ?? defaults.roleId;
|
|
8701
8982
|
assertSafeId(roleId, "roleId");
|
|
8702
8983
|
const runtime = parseRuntime(input.runtime, defaults.defaultRuntime);
|
|
@@ -9220,12 +9501,12 @@ async function readTeamOverlayAgentSummaries(input) {
|
|
|
9220
9501
|
})));
|
|
9221
9502
|
}
|
|
9222
9503
|
async function listDefaultTeamOverlayAssets(assetsRootDir) {
|
|
9223
|
-
const teamPath =
|
|
9224
|
-
const agentsDir =
|
|
9504
|
+
const teamPath = join13(assetsRootDir, "team", "team.md");
|
|
9505
|
+
const agentsDir = join13(assetsRootDir, "team", "agents");
|
|
9225
9506
|
await assertReadableFile(teamPath, "Default team asset");
|
|
9226
9507
|
let entries;
|
|
9227
9508
|
try {
|
|
9228
|
-
entries = await
|
|
9509
|
+
entries = await readdir8(agentsDir, { withFileTypes: true });
|
|
9229
9510
|
} catch (error) {
|
|
9230
9511
|
throw new Error(`Cannot read default team agents directory ${agentsDir}: ${describeError2(error)}`);
|
|
9231
9512
|
}
|
|
@@ -9237,7 +9518,7 @@ async function listDefaultTeamOverlayAssets(assetsRootDir) {
|
|
|
9237
9518
|
{ kind: "team", sourcePath: teamPath, name: "team.md" },
|
|
9238
9519
|
...agentFiles.map((name) => ({
|
|
9239
9520
|
kind: "agent",
|
|
9240
|
-
sourcePath:
|
|
9521
|
+
sourcePath: join13(agentsDir, name),
|
|
9241
9522
|
name
|
|
9242
9523
|
}))
|
|
9243
9524
|
];
|
|
@@ -9260,8 +9541,8 @@ async function writeTextFileIfMissing(path, content) {
|
|
|
9260
9541
|
throw new Error(`Cannot inspect ${path}: ${describeError2(error)}`);
|
|
9261
9542
|
}
|
|
9262
9543
|
}
|
|
9263
|
-
await
|
|
9264
|
-
await
|
|
9544
|
+
await mkdir8(dirname8(path), { recursive: true });
|
|
9545
|
+
await writeFile7(path, content.endsWith(`
|
|
9265
9546
|
`) ? content : `${content}
|
|
9266
9547
|
`, "utf8");
|
|
9267
9548
|
return true;
|
|
@@ -9269,7 +9550,7 @@ async function writeTextFileIfMissing(path, content) {
|
|
|
9269
9550
|
function parseTeamDefinitionAgents(value) {
|
|
9270
9551
|
if (value === undefined)
|
|
9271
9552
|
return {};
|
|
9272
|
-
if (!
|
|
9553
|
+
if (!isRecord8(value))
|
|
9273
9554
|
throw new Error("team.md agents must be a role-id map.");
|
|
9274
9555
|
const agents = {};
|
|
9275
9556
|
for (const [roleId, reference] of Object.entries(value)) {
|
|
@@ -9335,7 +9616,7 @@ function parseSimpleYaml(content) {
|
|
|
9335
9616
|
throw new Error(`Invalid YAML line: ${trimmed}`);
|
|
9336
9617
|
const key = trimmed.slice(0, separator).trim();
|
|
9337
9618
|
const rawValue = trimmed.slice(separator + 1).trim();
|
|
9338
|
-
if (!
|
|
9619
|
+
if (!isRecord8(parent))
|
|
9339
9620
|
throw new Error(`Invalid YAML parent for key ${key}.`);
|
|
9340
9621
|
if (rawValue === "") {
|
|
9341
9622
|
const next = findNextYamlContentLine(lines, index + 1);
|
|
@@ -9397,10 +9678,10 @@ function appendPromptBlock(prompt, block) {
|
|
|
9397
9678
|
${block.trim()}`;
|
|
9398
9679
|
}
|
|
9399
9680
|
function resolveGlobalTeamMarkdownPath(homeDir) {
|
|
9400
|
-
return
|
|
9681
|
+
return join13(resolveEvoDevPaths(homeDir).rootDir, "team", "team.md");
|
|
9401
9682
|
}
|
|
9402
9683
|
function parseRolePermissions(value, main) {
|
|
9403
|
-
const input =
|
|
9684
|
+
const input = isRecord8(value) ? value : {};
|
|
9404
9685
|
return {
|
|
9405
9686
|
writeMode: parseWriteMode(input.writeMode, "repo-write"),
|
|
9406
9687
|
canUseTeamsMcp: optionalBoolean2(input.canUseTeamsMcp) ?? true,
|
|
@@ -9409,14 +9690,14 @@ function parseRolePermissions(value, main) {
|
|
|
9409
9690
|
};
|
|
9410
9691
|
}
|
|
9411
9692
|
function parseRolePolicy(value, recordTranscript) {
|
|
9412
|
-
const input =
|
|
9693
|
+
const input = isRecord8(value) ? value : {};
|
|
9413
9694
|
return {
|
|
9414
9695
|
roleInstancePolicy: "single-per-role",
|
|
9415
9696
|
recordTranscript: optionalBoolean2(input.recordTranscript) ?? recordTranscript
|
|
9416
9697
|
};
|
|
9417
9698
|
}
|
|
9418
9699
|
function parseTeamRunRecord(value) {
|
|
9419
|
-
if (!
|
|
9700
|
+
if (!isRecord8(value) || value.version !== 1)
|
|
9420
9701
|
throw new Error("Invalid team run record.");
|
|
9421
9702
|
const run = value;
|
|
9422
9703
|
assertSafeId(run.runId, "runId");
|
|
@@ -9426,7 +9707,7 @@ function parseTeamRunRecord(value) {
|
|
|
9426
9707
|
return run;
|
|
9427
9708
|
}
|
|
9428
9709
|
function parseTeamAgentRecord(value) {
|
|
9429
|
-
if (!
|
|
9710
|
+
if (!isRecord8(value) || value.version !== 1)
|
|
9430
9711
|
throw new Error("Invalid team agent record.");
|
|
9431
9712
|
const agent = value;
|
|
9432
9713
|
assertSafeId(agent.agentId, "agentId");
|
|
@@ -9447,7 +9728,7 @@ function parseTeamAgentRecord(value) {
|
|
|
9447
9728
|
].includes(agent.status)) {
|
|
9448
9729
|
throw new Error("Invalid agent status.");
|
|
9449
9730
|
}
|
|
9450
|
-
const nativeSessionValue =
|
|
9731
|
+
const nativeSessionValue = isRecord8(value.nativeSession) ? value.nativeSession : {};
|
|
9451
9732
|
return {
|
|
9452
9733
|
...agent,
|
|
9453
9734
|
nativeSession: {
|
|
@@ -9457,7 +9738,7 @@ function parseTeamAgentRecord(value) {
|
|
|
9457
9738
|
};
|
|
9458
9739
|
}
|
|
9459
9740
|
function parseTeamMessageRecord(value) {
|
|
9460
|
-
if (!
|
|
9741
|
+
if (!isRecord8(value) || value.version !== 1)
|
|
9461
9742
|
throw new Error("Invalid team message record.");
|
|
9462
9743
|
const message = value;
|
|
9463
9744
|
assertSafeId(message.messageId, "messageId");
|
|
@@ -9486,7 +9767,7 @@ function parseTeamPendingMessageRecord(value) {
|
|
|
9486
9767
|
};
|
|
9487
9768
|
}
|
|
9488
9769
|
function parseTeamMessageListFile(value) {
|
|
9489
|
-
if (!
|
|
9770
|
+
if (!isRecord8(value) || value.version !== 1)
|
|
9490
9771
|
throw new Error("Invalid team message list.");
|
|
9491
9772
|
const messages = Array.isArray(value.messages) ? value.messages.map(parseTeamPendingMessageRecord) : [];
|
|
9492
9773
|
return {
|
|
@@ -9496,7 +9777,7 @@ function parseTeamMessageListFile(value) {
|
|
|
9496
9777
|
};
|
|
9497
9778
|
}
|
|
9498
9779
|
function parseTeamEventRecord(value) {
|
|
9499
|
-
if (!
|
|
9780
|
+
if (!isRecord8(value) || value.version !== 1)
|
|
9500
9781
|
throw new Error("Invalid team event record.");
|
|
9501
9782
|
const event = value;
|
|
9502
9783
|
assertSafeId(event.eventId, "eventId");
|
|
@@ -9638,10 +9919,10 @@ async function resolveTeamRoleNativeAgentBinding(input) {
|
|
|
9638
9919
|
return bindings.find((binding) => binding.roleId === input.roleId) ?? null;
|
|
9639
9920
|
}
|
|
9640
9921
|
function resolveGlobalTeamBindingPath(homeDir) {
|
|
9641
|
-
return
|
|
9922
|
+
return join13(resolveEvoDevPaths(homeDir).rootDir, "team", "roles.json");
|
|
9642
9923
|
}
|
|
9643
9924
|
function resolveProjectTeamBindingPath(homeDir, projectKey) {
|
|
9644
|
-
return
|
|
9925
|
+
return join13(resolveEvoDevPaths(homeDir).rootDir, "projects", safeSlug(projectKey), "team.json");
|
|
9645
9926
|
}
|
|
9646
9927
|
function resolveTeamBindingWritePath(input) {
|
|
9647
9928
|
if (input.scope === "global") {
|
|
@@ -9668,7 +9949,7 @@ async function readTeamBindingConfig(path) {
|
|
|
9668
9949
|
}
|
|
9669
9950
|
}
|
|
9670
9951
|
function parseTeamBindingConfig(value) {
|
|
9671
|
-
if (!
|
|
9952
|
+
if (!isRecord8(value) || value.version !== 1)
|
|
9672
9953
|
throw new Error("Invalid team binding config.");
|
|
9673
9954
|
const updatedAt = optionalString5(value.updatedAt) ?? "unknown";
|
|
9674
9955
|
const rolesValue = Array.isArray(value.roles) ? value.roles : [];
|
|
@@ -9679,7 +9960,7 @@ function parseTeamBindingConfig(value) {
|
|
|
9679
9960
|
};
|
|
9680
9961
|
}
|
|
9681
9962
|
function parseTeamBindingRecord(value) {
|
|
9682
|
-
if (!
|
|
9963
|
+
if (!isRecord8(value) || value.version !== 1) {
|
|
9683
9964
|
throw new Error("Invalid team binding record.");
|
|
9684
9965
|
}
|
|
9685
9966
|
const roleId = optionalString5(value.roleId);
|
|
@@ -9724,12 +10005,12 @@ async function pathExists5(path) {
|
|
|
9724
10005
|
}
|
|
9725
10006
|
}
|
|
9726
10007
|
async function writeJson3(path, value) {
|
|
9727
|
-
await
|
|
9728
|
-
await
|
|
10008
|
+
await mkdir8(dirname8(path), { recursive: true });
|
|
10009
|
+
await writeFile7(path, `${JSON.stringify(value, null, 2)}
|
|
9729
10010
|
`, "utf8");
|
|
9730
10011
|
}
|
|
9731
10012
|
async function appendJsonLine2(path, value) {
|
|
9732
|
-
await
|
|
10013
|
+
await mkdir8(dirname8(path), { recursive: true });
|
|
9733
10014
|
await appendFile3(path, `${JSON.stringify(value)}
|
|
9734
10015
|
`, "utf8");
|
|
9735
10016
|
}
|
|
@@ -9767,7 +10048,7 @@ function optionalNullableString(value) {
|
|
|
9767
10048
|
function optionalBoolean2(value) {
|
|
9768
10049
|
return typeof value === "boolean" ? value : undefined;
|
|
9769
10050
|
}
|
|
9770
|
-
function
|
|
10051
|
+
function isRecord8(value) {
|
|
9771
10052
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
9772
10053
|
}
|
|
9773
10054
|
function describeError2(error) {
|
|
@@ -9871,9 +10152,9 @@ function parseHookSettings(value) {
|
|
|
9871
10152
|
const defaults = createDefaultHookSettings();
|
|
9872
10153
|
if (value === undefined || value === null)
|
|
9873
10154
|
return defaults;
|
|
9874
|
-
if (!
|
|
10155
|
+
if (!isRecord9(value))
|
|
9875
10156
|
throw new Error("Invalid hooks settings; expected object.");
|
|
9876
|
-
const observability =
|
|
10157
|
+
const observability = isRecord9(value.observability) ? value.observability : undefined;
|
|
9877
10158
|
optionalBoolean3(observability?.metadataOnly, defaults.observability.metadataOnly, "hooks.observability.metadataOnly");
|
|
9878
10159
|
optionalBoolean3(observability?.rawPayloadStorage, defaults.observability.rawPayloadStorage, "hooks.observability.rawPayloadStorage");
|
|
9879
10160
|
return {
|
|
@@ -10004,11 +10285,11 @@ function formatHookEventDryRun(event) {
|
|
|
10004
10285
|
`);
|
|
10005
10286
|
}
|
|
10006
10287
|
function resolveHookRuntimeSessionPaths(input) {
|
|
10007
|
-
const sessionDir =
|
|
10288
|
+
const sessionDir = join14(input.homeDir, ".evodev", "STATE", "hooks", "sessions", input.sessionKey);
|
|
10008
10289
|
return {
|
|
10009
10290
|
sessionDir,
|
|
10010
|
-
bindingPath:
|
|
10011
|
-
contractPath:
|
|
10291
|
+
bindingPath: join14(sessionDir, "binding.json"),
|
|
10292
|
+
contractPath: join14(sessionDir, "contract.json")
|
|
10012
10293
|
};
|
|
10013
10294
|
}
|
|
10014
10295
|
async function handleHookRuntime(input) {
|
|
@@ -10126,7 +10407,7 @@ async function recordEvolutionTriggerFromHook(input) {
|
|
|
10126
10407
|
return { stateWrites: [], warnings: [] };
|
|
10127
10408
|
return {
|
|
10128
10409
|
stateWrites: [
|
|
10129
|
-
|
|
10410
|
+
join14(input.homeDir, ".evodev", "state", "evolution", trigger.projectKey, trigger.runId, "triggers", `${trigger.id}.json`)
|
|
10130
10411
|
],
|
|
10131
10412
|
warnings: []
|
|
10132
10413
|
};
|
|
@@ -10445,7 +10726,7 @@ function hookOutput(eventName, output) {
|
|
|
10445
10726
|
function appendAdditionalContext(output, eventName, context) {
|
|
10446
10727
|
if (output === null)
|
|
10447
10728
|
return hookOutput(eventName, { additionalContext: context });
|
|
10448
|
-
const hookSpecificOutput =
|
|
10729
|
+
const hookSpecificOutput = isRecord9(output.hookSpecificOutput) ? output.hookSpecificOutput : {};
|
|
10449
10730
|
const previous = typeof hookSpecificOutput.additionalContext === "string" ? hookSpecificOutput.additionalContext : "";
|
|
10450
10731
|
return {
|
|
10451
10732
|
...output,
|
|
@@ -10594,8 +10875,8 @@ function resolveHookSessionKey(payload) {
|
|
|
10594
10875
|
return `session-${sha256Short(source)}`;
|
|
10595
10876
|
}
|
|
10596
10877
|
async function writeJsonFile2(path, value) {
|
|
10597
|
-
await
|
|
10598
|
-
await
|
|
10878
|
+
await mkdir9(dirname9(path), { recursive: true });
|
|
10879
|
+
await writeFile8(path, `${JSON.stringify(value, null, 2)}
|
|
10599
10880
|
`, "utf8");
|
|
10600
10881
|
}
|
|
10601
10882
|
function optionalPayloadString(value) {
|
|
@@ -10620,9 +10901,9 @@ function normalizeHookEventType(type, warnings) {
|
|
|
10620
10901
|
throw new Error(`Unsupported hook event type: ${type}`);
|
|
10621
10902
|
}
|
|
10622
10903
|
function parseHookTargetSettings(value, defaults, target) {
|
|
10623
|
-
const targets =
|
|
10624
|
-
const targetSettings =
|
|
10625
|
-
const events =
|
|
10904
|
+
const targets = isRecord9(value) ? value : {};
|
|
10905
|
+
const targetSettings = isRecord9(targets[target]) ? targets[target] : {};
|
|
10906
|
+
const events = isRecord9(targetSettings.events) ? targetSettings.events : {};
|
|
10626
10907
|
const parsedEvents = { ...defaults.events };
|
|
10627
10908
|
for (const eventType of CANONICAL_HOOK_EVENT_TYPES) {
|
|
10628
10909
|
parsedEvents[eventType] = optionalBoolean3(events[eventType], defaults.events[eventType], `hooks.targets.${target}.events.${eventType}`);
|
|
@@ -10634,7 +10915,7 @@ function parseHookTargetSettings(value, defaults, target) {
|
|
|
10634
10915
|
}
|
|
10635
10916
|
function extractMetadata(type, payload, redactions) {
|
|
10636
10917
|
const metadata = {};
|
|
10637
|
-
const toolInput =
|
|
10918
|
+
const toolInput = isRecord9(payload.tool_input) ? payload.tool_input : isRecord9(payload.toolInput) ? payload.toolInput : {};
|
|
10638
10919
|
const toolName = optionalSanitizedString(payload.tool_name ?? payload.toolName, redactions);
|
|
10639
10920
|
if (toolName !== null)
|
|
10640
10921
|
metadata.toolName = toolName;
|
|
@@ -10712,7 +10993,7 @@ function optionalNumber(value) {
|
|
|
10712
10993
|
function formatMetadataValue(value) {
|
|
10713
10994
|
return Array.isArray(value) ? value.join(",") : String(value);
|
|
10714
10995
|
}
|
|
10715
|
-
function
|
|
10996
|
+
function isRecord9(value) {
|
|
10716
10997
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
10717
10998
|
}
|
|
10718
10999
|
|
|
@@ -11014,20 +11295,20 @@ function expectNonNegativeInteger(value, path) {
|
|
|
11014
11295
|
return value;
|
|
11015
11296
|
}
|
|
11016
11297
|
// packages/core/src/config/store.ts
|
|
11017
|
-
import { mkdir as
|
|
11018
|
-
import { dirname as
|
|
11298
|
+
import { mkdir as mkdir10, readFile as readFile13, writeFile as writeFile9 } from "node:fs/promises";
|
|
11299
|
+
import { dirname as dirname10 } from "node:path";
|
|
11019
11300
|
function createCoreConfigStore(homeDir) {
|
|
11020
11301
|
const paths2 = resolveEvoDevPaths(homeDir);
|
|
11021
11302
|
return {
|
|
11022
11303
|
paths: paths2,
|
|
11023
11304
|
async ensureBaseDirs() {
|
|
11024
|
-
await
|
|
11025
|
-
await
|
|
11026
|
-
await
|
|
11027
|
-
await
|
|
11028
|
-
await
|
|
11029
|
-
await
|
|
11030
|
-
await
|
|
11305
|
+
await mkdir10(paths2.stateDir, { recursive: true });
|
|
11306
|
+
await mkdir10(paths2.logsDir, { recursive: true });
|
|
11307
|
+
await mkdir10(paths2.knowledgeDir, { recursive: true });
|
|
11308
|
+
await mkdir10(paths2.evosCasesDir, { recursive: true });
|
|
11309
|
+
await mkdir10(paths2.roleAgentsDir, { recursive: true });
|
|
11310
|
+
await mkdir10(paths2.teamsDir, { recursive: true });
|
|
11311
|
+
await mkdir10(paths2.runsDir, { recursive: true });
|
|
11031
11312
|
},
|
|
11032
11313
|
async ensureKnowledgeBase() {
|
|
11033
11314
|
await ensureKnowledgeBaseFiles(paths2);
|
|
@@ -11075,8 +11356,8 @@ async function initializeCoreConfig(homeDir) {
|
|
|
11075
11356
|
return store;
|
|
11076
11357
|
}
|
|
11077
11358
|
async function ensureKnowledgeBaseFiles(paths2) {
|
|
11078
|
-
await
|
|
11079
|
-
await
|
|
11359
|
+
await mkdir10(paths2.knowledgeDir, { recursive: true });
|
|
11360
|
+
await mkdir10(paths2.evosCasesDir, { recursive: true });
|
|
11080
11361
|
await ensureOkfKnowledgeBase(paths2.homeDir);
|
|
11081
11362
|
await writeTextIfMissing2(`${paths2.knowledgeDir}/README.md`, [
|
|
11082
11363
|
"# EvoDev Knowledge",
|
|
@@ -11205,7 +11486,7 @@ async function writeIndexIfMissingOrMigrate(filePath, kind, defaults) {
|
|
|
11205
11486
|
} catch (error) {
|
|
11206
11487
|
throw new EvoDevConfigError(`Invalid bootstrap index JSON (${describeFileError2(error)})`, filePath);
|
|
11207
11488
|
}
|
|
11208
|
-
if (!
|
|
11489
|
+
if (!isRecord10(existing) || existing.kind !== kind)
|
|
11209
11490
|
return;
|
|
11210
11491
|
const migrated = { ...defaults, ...existing };
|
|
11211
11492
|
if (Object.keys(defaults).every((key) => (key in existing)))
|
|
@@ -11217,16 +11498,16 @@ async function writeTextIfMissing2(filePath, value) {
|
|
|
11217
11498
|
await readFile13(filePath, "utf8");
|
|
11218
11499
|
} catch (error) {
|
|
11219
11500
|
if (isNodeError2(error) && error.code === "ENOENT") {
|
|
11220
|
-
await
|
|
11221
|
-
await
|
|
11501
|
+
await mkdir10(dirname10(filePath), { recursive: true });
|
|
11502
|
+
await writeFile9(filePath, value, "utf8");
|
|
11222
11503
|
return;
|
|
11223
11504
|
}
|
|
11224
11505
|
throw new EvoDevConfigError(`Cannot inspect config file (${describeFileError2(error)})`, filePath);
|
|
11225
11506
|
}
|
|
11226
11507
|
}
|
|
11227
11508
|
async function writeJsonFile3(filePath, value) {
|
|
11228
|
-
await
|
|
11229
|
-
await
|
|
11509
|
+
await mkdir10(dirname10(filePath), { recursive: true });
|
|
11510
|
+
await writeFile9(filePath, `${JSON.stringify(value, null, 2)}
|
|
11230
11511
|
`, "utf8");
|
|
11231
11512
|
}
|
|
11232
11513
|
function describeFileError2(error) {
|
|
@@ -11238,14 +11519,14 @@ function describeFileError2(error) {
|
|
|
11238
11519
|
function isNodeError2(error) {
|
|
11239
11520
|
return error instanceof Error && "code" in error;
|
|
11240
11521
|
}
|
|
11241
|
-
function
|
|
11522
|
+
function isRecord10(value) {
|
|
11242
11523
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
11243
11524
|
}
|
|
11244
11525
|
// packages/core/src/daemon/index.ts
|
|
11245
11526
|
import { randomBytes } from "node:crypto";
|
|
11246
|
-
import { mkdir as
|
|
11527
|
+
import { mkdir as mkdir13, readFile as readFile16, readdir as readdir11, rm as rm4, stat as stat9, writeFile as writeFile12 } from "node:fs/promises";
|
|
11247
11528
|
import { createServer } from "node:http";
|
|
11248
|
-
import { dirname as
|
|
11529
|
+
import { dirname as dirname13, join as join19 } from "node:path";
|
|
11249
11530
|
|
|
11250
11531
|
// packages/core/src/evolution/control/index.ts
|
|
11251
11532
|
var exports_control = {};
|
|
@@ -11386,6 +11667,7 @@ function formatEvolutionReviewSnapshot(snapshot) {
|
|
|
11386
11667
|
// packages/core/src/evolution/processor/index.ts
|
|
11387
11668
|
var exports_processor = {};
|
|
11388
11669
|
__export(exports_processor, {
|
|
11670
|
+
writeEvolutionRepoProposal: () => writeEvolutionRepoProposal,
|
|
11389
11671
|
writeEvolutionDistillationBatch: () => writeEvolutionDistillationBatch,
|
|
11390
11672
|
processEvolutionTriggers: () => processEvolutionTriggers,
|
|
11391
11673
|
createEvolutionDistillationBatch: () => createEvolutionDistillationBatch,
|
|
@@ -11394,8 +11676,8 @@ __export(exports_processor, {
|
|
|
11394
11676
|
});
|
|
11395
11677
|
|
|
11396
11678
|
// packages/core/src/evolution/evidence/analysis.ts
|
|
11397
|
-
import { readFile as readFile14, readdir as
|
|
11398
|
-
import { basename as basename3, join as
|
|
11679
|
+
import { readFile as readFile14, readdir as readdir9 } from "node:fs/promises";
|
|
11680
|
+
import { basename as basename3, join as join15 } from "node:path";
|
|
11399
11681
|
async function analyzeEvolutionRun(input) {
|
|
11400
11682
|
const projectKey = resolveEvolutionProjectKey(input);
|
|
11401
11683
|
const runId = sanitizeId(input.runId);
|
|
@@ -11405,17 +11687,17 @@ async function analyzeEvolutionRun(input) {
|
|
|
11405
11687
|
const linkedTraceRefIds = new Set;
|
|
11406
11688
|
const events = [];
|
|
11407
11689
|
const logsDir = resolveEvoDevPaths(input.homeDir).logsDir;
|
|
11408
|
-
const projectRunAgentsDir =
|
|
11409
|
-
const legacyProjectRunAgentsDir =
|
|
11410
|
-
const agentDirs = await pathExists(projectRunAgentsDir) ? await
|
|
11411
|
-
const legacyAgentDirs = await pathExists(legacyProjectRunAgentsDir) ? await
|
|
11690
|
+
const projectRunAgentsDir = join15(logsDir, "teams", projectKey, runId, "agents");
|
|
11691
|
+
const legacyProjectRunAgentsDir = join15(logsDir, projectKey, runId, "agents");
|
|
11692
|
+
const agentDirs = await pathExists(projectRunAgentsDir) ? await readdir9(projectRunAgentsDir, { withFileTypes: true }) : [];
|
|
11693
|
+
const legacyAgentDirs = await pathExists(legacyProjectRunAgentsDir) ? await readdir9(legacyProjectRunAgentsDir, { withFileTypes: true }) : [];
|
|
11412
11694
|
if (agentDirs.length === 0 && legacyAgentDirs.length === 0) {
|
|
11413
11695
|
warnings.push(`No agent execution event directory found: ${displayPath(input.homeDir, projectRunAgentsDir)}`);
|
|
11414
11696
|
} else {
|
|
11415
11697
|
const rolesWithExecutionEvents = new Set;
|
|
11416
11698
|
for (const roleDir of agentDirs.filter((entry) => entry.isDirectory())) {
|
|
11417
11699
|
const roleId = sanitizeId(roleDir.name);
|
|
11418
|
-
const eventsPath =
|
|
11700
|
+
const eventsPath = join15(projectRunAgentsDir, roleDir.name, "events.jsonl");
|
|
11419
11701
|
if (!await pathExists(eventsPath))
|
|
11420
11702
|
continue;
|
|
11421
11703
|
rolesWithExecutionEvents.add(roleId);
|
|
@@ -11443,7 +11725,7 @@ async function analyzeEvolutionRun(input) {
|
|
|
11443
11725
|
const roleId = sanitizeId(roleDir.name);
|
|
11444
11726
|
if (rolesWithExecutionEvents.has(roleId))
|
|
11445
11727
|
continue;
|
|
11446
|
-
const tracePath =
|
|
11728
|
+
const tracePath = join15(legacyProjectRunAgentsDir, roleDir.name, "trace.log");
|
|
11447
11729
|
if (!await pathExists(tracePath))
|
|
11448
11730
|
continue;
|
|
11449
11731
|
const sourceRef = {
|
|
@@ -11807,7 +12089,7 @@ function resolveEvolutionProjectKey(input) {
|
|
|
11807
12089
|
throw new Error("Missing project scope: pass --project <projectKey> or --project-dir <path>.");
|
|
11808
12090
|
}
|
|
11809
12091
|
// packages/core/src/evolution/processor/distillation.ts
|
|
11810
|
-
import { join as
|
|
12092
|
+
import { join as join16 } from "node:path";
|
|
11811
12093
|
function createEvolutionDistillationBatch(input) {
|
|
11812
12094
|
validateEvolutionEvidenceWindow(input.evidenceWindow);
|
|
11813
12095
|
const createdAt = normalizeTimestamp(input.now);
|
|
@@ -11839,7 +12121,7 @@ function createEvolutionDistillationBatch(input) {
|
|
|
11839
12121
|
relations: {
|
|
11840
12122
|
relatedKnowledgeIds: [],
|
|
11841
12123
|
evosCaseIds: [createStableId("evo", [input.evidenceWindow.id, "case"])],
|
|
11842
|
-
proposalIds:
|
|
12124
|
+
proposalIds: [],
|
|
11843
12125
|
supersedes: []
|
|
11844
12126
|
},
|
|
11845
12127
|
privacy: createPrivacyFields(),
|
|
@@ -11876,29 +12158,7 @@ function createEvolutionDistillationBatch(input) {
|
|
|
11876
12158
|
privacy: createPrivacyFields()
|
|
11877
12159
|
})
|
|
11878
12160
|
];
|
|
11879
|
-
const repoProposals =
|
|
11880
|
-
createEvolutionRepoProposal({
|
|
11881
|
-
id: createStableId("proposal", [input.evidenceWindow.id, "engineering-practice"]),
|
|
11882
|
-
kind: "engineering-practice",
|
|
11883
|
-
projectKey: input.evidenceWindow.projectKey,
|
|
11884
|
-
title: `Review repeated failure signals for ${input.evidenceWindow.runId}`,
|
|
11885
|
-
summary: "Failure or issue signals appeared in redacted run evidence; review whether repository rules, tests, skills, or role agents need improvement.",
|
|
11886
|
-
rationale: "EvoDev stores this as a proposal only. It must not modify the user repository until an explicit apply command is implemented and invoked.",
|
|
11887
|
-
roleTags: roleIds,
|
|
11888
|
-
tags: ["proposal", "failure-signal"],
|
|
11889
|
-
reviewState: "pending",
|
|
11890
|
-
confidence: "low",
|
|
11891
|
-
targetRepoPath: null,
|
|
11892
|
-
plannedFiles: [],
|
|
11893
|
-
apply: {
|
|
11894
|
-
autoApply: false,
|
|
11895
|
-
requiresExplicitCommand: true,
|
|
11896
|
-
rollbackPlan: "No repository files are changed by this proposal."
|
|
11897
|
-
},
|
|
11898
|
-
provenance: createProvenance(input.evidenceWindow, createdAt, sourceRefs),
|
|
11899
|
-
privacy: createPrivacyFields()
|
|
11900
|
-
})
|
|
11901
|
-
];
|
|
12161
|
+
const repoProposals = [];
|
|
11902
12162
|
const batch = {
|
|
11903
12163
|
schemaVersion: 1,
|
|
11904
12164
|
id: createStableId("batch", [input.evidenceWindow.id, createdAt]),
|
|
@@ -11926,19 +12186,19 @@ async function writeEvolutionDistillationBatch(input) {
|
|
|
11926
12186
|
await writeJsonFile(paths2.batchPath, input.batch, { overwrite });
|
|
11927
12187
|
const knowledgePaths = [];
|
|
11928
12188
|
for (const record of input.batch.knowledgeRecords) {
|
|
11929
|
-
const path =
|
|
12189
|
+
const path = join16(paths2.knowledgeRecordsDir, `${record.id}.json`);
|
|
11930
12190
|
await writeJsonFile(path, record, { overwrite });
|
|
11931
12191
|
knowledgePaths.push(path);
|
|
11932
12192
|
}
|
|
11933
12193
|
const evosCasePaths = [];
|
|
11934
12194
|
for (const evosCase of input.batch.evosCases) {
|
|
11935
|
-
const path =
|
|
12195
|
+
const path = join16(paths2.evosCasesProjectDir, `${evosCase.id}.json`);
|
|
11936
12196
|
await writeJsonFile(path, evosCase, { overwrite });
|
|
11937
12197
|
evosCasePaths.push(path);
|
|
11938
12198
|
}
|
|
11939
12199
|
const repoProposalPaths = [];
|
|
11940
12200
|
for (const proposal of input.batch.repoProposals) {
|
|
11941
|
-
const path =
|
|
12201
|
+
const path = join16(paths2.repoProposalsDir, `${proposal.id}.json`);
|
|
11942
12202
|
await writeJsonFile(path, proposal, { overwrite });
|
|
11943
12203
|
repoProposalPaths.push(path);
|
|
11944
12204
|
}
|
|
@@ -11958,7 +12218,7 @@ async function writeEvolutionDistillationBatch(input) {
|
|
|
11958
12218
|
const evosCasesRootDir = resolveEvoDevPaths(input.homeDir).evosCasesDir;
|
|
11959
12219
|
const evosProjectKeys = await listDirectoryNames(evosCasesRootDir);
|
|
11960
12220
|
const evosProjects = await Promise.all(evosProjectKeys.map(async (projectKey) => {
|
|
11961
|
-
const cases = await readJsonFiles(
|
|
12221
|
+
const cases = await readJsonFiles(join16(evosCasesRootDir, projectKey), parseEvosCase);
|
|
11962
12222
|
return {
|
|
11963
12223
|
projectKey,
|
|
11964
12224
|
cases: cases.map((evosCase) => ({
|
|
@@ -11996,6 +12256,33 @@ async function writeEvolutionDistillationBatch(input) {
|
|
|
11996
12256
|
evosIndexPath: paths2.evosIndexPath
|
|
11997
12257
|
};
|
|
11998
12258
|
}
|
|
12259
|
+
async function writeEvolutionRepoProposal(input) {
|
|
12260
|
+
validateEvolutionRepoProposal(input.proposal);
|
|
12261
|
+
if (!hasConcreteRepoProposalChanges(input.proposal)) {
|
|
12262
|
+
throw new Error("Repo proposal must include a target repository and concrete file changes.");
|
|
12263
|
+
}
|
|
12264
|
+
const paths2 = resolveEvolutionPaths({
|
|
12265
|
+
homeDir: input.homeDir,
|
|
12266
|
+
projectKey: input.proposal.projectKey,
|
|
12267
|
+
runId: input.proposal.provenance.runId
|
|
12268
|
+
});
|
|
12269
|
+
const proposalPath = join16(paths2.repoProposalsDir, `${input.proposal.id}.json`);
|
|
12270
|
+
await writeJsonFile(proposalPath, input.proposal, { overwrite: false });
|
|
12271
|
+
const allRunProposals = await readJsonFiles(paths2.repoProposalsDir, parseRepoProposal);
|
|
12272
|
+
await writeJsonFile(paths2.repoProposalsIndexPath, {
|
|
12273
|
+
schemaVersion: 1,
|
|
12274
|
+
projectKey: input.proposal.projectKey,
|
|
12275
|
+
runId: input.proposal.provenance.runId,
|
|
12276
|
+
updatedAt: input.proposal.provenance.createdAt,
|
|
12277
|
+
proposals: allRunProposals.map((proposal) => ({
|
|
12278
|
+
id: proposal.id,
|
|
12279
|
+
kind: proposal.kind,
|
|
12280
|
+
title: proposal.title,
|
|
12281
|
+
reviewState: proposal.reviewState
|
|
12282
|
+
}))
|
|
12283
|
+
}, { overwrite: true });
|
|
12284
|
+
return { proposalPath, indexPath: paths2.repoProposalsIndexPath };
|
|
12285
|
+
}
|
|
11999
12286
|
async function activateEvolutionDistillationBatch(input) {
|
|
12000
12287
|
validateEvolutionDistillationBatch(input.batch);
|
|
12001
12288
|
const paths2 = resolveEvolutionPaths({
|
|
@@ -12020,20 +12307,20 @@ async function activateEvolutionDistillationBatch(input) {
|
|
|
12020
12307
|
const matchingCandidate = plan.candidates.find((candidate) => candidate.id === evosCase.id);
|
|
12021
12308
|
const persistedEvosCase = matchingCandidate?.decision === "auto-accept" ? { ...evosCase, reviewState: "auto-accepted" } : matchingCandidate?.decision === "needs-human" ? { ...evosCase, reviewState: "needs-human" } : evosCase;
|
|
12022
12309
|
validateEvolutionEvosCase(persistedEvosCase);
|
|
12023
|
-
const path =
|
|
12310
|
+
const path = join16(paths2.evosCasesProjectDir, `${evosCase.id}.json`);
|
|
12024
12311
|
await writeJsonFile(path, persistedEvosCase, { overwrite });
|
|
12025
12312
|
evosCasePaths.push(path);
|
|
12026
12313
|
}
|
|
12027
12314
|
const repoProposalPaths = [];
|
|
12028
12315
|
for (const proposal of input.batch.repoProposals) {
|
|
12029
|
-
const path =
|
|
12316
|
+
const path = join16(paths2.repoProposalsDir, `${proposal.id}.json`);
|
|
12030
12317
|
await writeJsonFile(path, proposal, { overwrite });
|
|
12031
12318
|
repoProposalPaths.push(path);
|
|
12032
12319
|
}
|
|
12033
12320
|
const evosCasesRootDir = resolveEvoDevPaths(input.homeDir).evosCasesDir;
|
|
12034
12321
|
const evosProjectKeys = await listDirectoryNames(evosCasesRootDir);
|
|
12035
12322
|
const evosProjects = await Promise.all(evosProjectKeys.map(async (projectKey) => {
|
|
12036
|
-
const cases = await readJsonFiles(
|
|
12323
|
+
const cases = await readJsonFiles(join16(evosCasesRootDir, projectKey), parseEvosCase);
|
|
12037
12324
|
return {
|
|
12038
12325
|
projectKey,
|
|
12039
12326
|
cases: cases.map((evosCase) => ({
|
|
@@ -12089,8 +12376,8 @@ function createProvenance(evidenceWindow, createdAt, sourceRefs) {
|
|
|
12089
12376
|
};
|
|
12090
12377
|
}
|
|
12091
12378
|
// packages/core/src/evolution/processor/process.ts
|
|
12092
|
-
import { mkdir as
|
|
12093
|
-
import { dirname as
|
|
12379
|
+
import { mkdir as mkdir11, rm as rm3, stat as stat7, writeFile as writeFile10 } from "node:fs/promises";
|
|
12380
|
+
import { dirname as dirname11, join as join17 } from "node:path";
|
|
12094
12381
|
var PROCESS_LOCK_STALE_MS2 = 5 * 60 * 1000;
|
|
12095
12382
|
async function processEvolutionTriggers(input) {
|
|
12096
12383
|
const now = normalizeTimestamp(input.now);
|
|
@@ -12153,16 +12440,11 @@ async function processEvolutionTriggers(input) {
|
|
|
12153
12440
|
attempts: (current) => current.attempts + 1
|
|
12154
12441
|
});
|
|
12155
12442
|
try {
|
|
12156
|
-
await writeSegmentReviewCandidate({
|
|
12157
|
-
homeDir: input.homeDir,
|
|
12158
|
-
trigger,
|
|
12159
|
-
now
|
|
12160
|
-
});
|
|
12161
12443
|
result.consumed += 1;
|
|
12162
12444
|
await updateSegmentTriggers(input.homeDir, [trigger], {
|
|
12163
12445
|
status: "consumed",
|
|
12164
12446
|
updatedAt: now,
|
|
12165
|
-
processedBatchId: createStableId("segment-
|
|
12447
|
+
processedBatchId: createStableId("segment-evidence", [trigger.id]),
|
|
12166
12448
|
lastError: null
|
|
12167
12449
|
});
|
|
12168
12450
|
} catch (error) {
|
|
@@ -12254,10 +12536,10 @@ async function processEvolutionTriggers(input) {
|
|
|
12254
12536
|
}
|
|
12255
12537
|
async function acquireEvolutionProcessLock(homeDir, now) {
|
|
12256
12538
|
const paths2 = resolveEvoDevPaths(homeDir);
|
|
12257
|
-
const lockPath =
|
|
12258
|
-
await
|
|
12539
|
+
const lockPath = join17(paths2.stateDir, "evolution", ".process.lock");
|
|
12540
|
+
await mkdir11(dirname11(lockPath), { recursive: true });
|
|
12259
12541
|
try {
|
|
12260
|
-
await
|
|
12542
|
+
await writeFile10(lockPath, `${JSON.stringify({
|
|
12261
12543
|
schemaVersion: 1,
|
|
12262
12544
|
kind: "evolution-process-lock",
|
|
12263
12545
|
createdAt: now,
|
|
@@ -12270,70 +12552,18 @@ async function acquireEvolutionProcessLock(homeDir, now) {
|
|
|
12270
12552
|
throw error;
|
|
12271
12553
|
const current = await stat7(lockPath).catch(() => null);
|
|
12272
12554
|
if (current !== null && Date.now() - current.mtimeMs > PROCESS_LOCK_STALE_MS2) {
|
|
12273
|
-
await
|
|
12555
|
+
await rm3(lockPath, { force: true });
|
|
12274
12556
|
return acquireEvolutionProcessLock(homeDir, now);
|
|
12275
12557
|
}
|
|
12276
12558
|
return null;
|
|
12277
12559
|
}
|
|
12278
12560
|
}
|
|
12279
12561
|
async function releaseEvolutionProcessLock(lock) {
|
|
12280
|
-
await
|
|
12281
|
-
}
|
|
12282
|
-
async function writeSegmentReviewCandidate(input) {
|
|
12283
|
-
const runId = input.trigger.runId ?? `session-${input.trigger.sessionKey}`;
|
|
12284
|
-
const paths2 = resolveEvolutionPaths({
|
|
12285
|
-
homeDir: input.homeDir,
|
|
12286
|
-
projectKey: input.trigger.projectKey,
|
|
12287
|
-
runId
|
|
12288
|
-
});
|
|
12289
|
-
const candidate = {
|
|
12290
|
-
schemaVersion: 1,
|
|
12291
|
-
kind: "evolution-review-candidate",
|
|
12292
|
-
id: createStableId("review", [input.trigger.id]),
|
|
12293
|
-
projectKey: input.trigger.projectKey,
|
|
12294
|
-
runId: paths2.runId,
|
|
12295
|
-
createdAt: input.now,
|
|
12296
|
-
candidateKind: "session-memory-segment",
|
|
12297
|
-
title: sanitizeText(`Review session segment ${input.trigger.reason}`),
|
|
12298
|
-
targetStore: "session-memory-segments",
|
|
12299
|
-
targetPath: input.trigger.segmentPath,
|
|
12300
|
-
stableKey: createStableId("segment-candidate", [
|
|
12301
|
-
input.trigger.projectKey,
|
|
12302
|
-
input.trigger.sessionKey,
|
|
12303
|
-
input.trigger.segmentId
|
|
12304
|
-
]),
|
|
12305
|
-
reviewState: "needs-human",
|
|
12306
|
-
reasons: [input.trigger.reason, input.trigger.strength],
|
|
12307
|
-
candidate: {
|
|
12308
|
-
segmentId: input.trigger.segmentId,
|
|
12309
|
-
sessionKey: input.trigger.sessionKey,
|
|
12310
|
-
roleId: input.trigger.roleId,
|
|
12311
|
-
summary: input.trigger.summary,
|
|
12312
|
-
reviewAction: "Open the referenced local session segment for raw evidence before approving memory or OKF changes.",
|
|
12313
|
-
directOkfWrite: false,
|
|
12314
|
-
localOnly: true
|
|
12315
|
-
},
|
|
12316
|
-
provenance: {
|
|
12317
|
-
runId: paths2.runId,
|
|
12318
|
-
evidenceWindowId: input.trigger.segmentId,
|
|
12319
|
-
evidenceRefs: [input.trigger.id, input.trigger.segmentId],
|
|
12320
|
-
createdBy: "evodev",
|
|
12321
|
-
rawLogsStored: false,
|
|
12322
|
-
rawPromptsStored: false,
|
|
12323
|
-
sourceDumpsStored: false,
|
|
12324
|
-
rawCommandOutputStored: false
|
|
12325
|
-
},
|
|
12326
|
-
privacy: createPrivacyFields()
|
|
12327
|
-
};
|
|
12328
|
-
validateEvolutionReviewCandidate(candidate);
|
|
12329
|
-
await writeJsonFile(join16(paths2.reviewCandidatesDir, `${candidate.id}.json`), candidate, {
|
|
12330
|
-
overwrite: true
|
|
12331
|
-
});
|
|
12332
|
-
return candidate;
|
|
12562
|
+
await rm3(lock.path, { force: true });
|
|
12333
12563
|
}
|
|
12334
12564
|
// packages/core/src/observability/index.ts
|
|
12335
|
-
import { mkdir as
|
|
12336
|
-
import { dirname as
|
|
12565
|
+
import { mkdir as mkdir12, readFile as readFile15, readdir as readdir10, stat as stat8, writeFile as writeFile11 } from "node:fs/promises";
|
|
12566
|
+
import { dirname as dirname12, join as join18 } from "node:path";
|
|
12337
12567
|
var EVENT_TYPE_TO_DIR = {
|
|
12338
12568
|
"verification.completed": "verification",
|
|
12339
12569
|
"workflow.step.planned": "workflows",
|
|
@@ -12395,14 +12625,14 @@ function validateObservabilityEvent(event) {
|
|
|
12395
12625
|
assertNoForbiddenContent(event);
|
|
12396
12626
|
}
|
|
12397
12627
|
function resolveObservabilityStorePaths(homeDir, type) {
|
|
12398
|
-
const rootDir =
|
|
12399
|
-
return { rootDir, eventsPath:
|
|
12628
|
+
const rootDir = join18(homeDir, ".evodev", "OBSERVABILITY", EVENT_TYPE_TO_DIR[type]);
|
|
12629
|
+
return { rootDir, eventsPath: join18(rootDir, "events.jsonl") };
|
|
12400
12630
|
}
|
|
12401
12631
|
async function appendObservabilityEvent(homeDir, event) {
|
|
12402
12632
|
validateObservabilityEvent(event);
|
|
12403
12633
|
const paths2 = resolveObservabilityStorePaths(homeDir, event.type);
|
|
12404
|
-
await
|
|
12405
|
-
await
|
|
12634
|
+
await mkdir12(dirname12(paths2.eventsPath), { recursive: true });
|
|
12635
|
+
await writeFile11(paths2.eventsPath, `${JSON.stringify(event)}
|
|
12406
12636
|
`, { encoding: "utf8", flag: "a" });
|
|
12407
12637
|
return paths2.eventsPath;
|
|
12408
12638
|
}
|
|
@@ -12424,7 +12654,7 @@ async function listObservabilityEvents(homeDir, type) {
|
|
|
12424
12654
|
return events.sort((left, right) => left.timestamp.localeCompare(right.timestamp));
|
|
12425
12655
|
}
|
|
12426
12656
|
async function dryRunObservabilityRetentionCleanup(homeDir) {
|
|
12427
|
-
const root =
|
|
12657
|
+
const root = join18(homeDir, ".evodev", "OBSERVABILITY");
|
|
12428
12658
|
const candidates = [];
|
|
12429
12659
|
if (!await pathExists6(root))
|
|
12430
12660
|
return { candidates, totalBytes: 0 };
|
|
@@ -12495,10 +12725,10 @@ function assertNoForbiddenContent(value) {
|
|
|
12495
12725
|
}
|
|
12496
12726
|
}
|
|
12497
12727
|
async function collectJsonlFiles(root) {
|
|
12498
|
-
const entries = await
|
|
12728
|
+
const entries = await readdir10(root, { withFileTypes: true });
|
|
12499
12729
|
const files = [];
|
|
12500
12730
|
for (const entry of entries) {
|
|
12501
|
-
const path =
|
|
12731
|
+
const path = join18(root, entry.name);
|
|
12502
12732
|
if (entry.isDirectory())
|
|
12503
12733
|
files.push(...await collectJsonlFiles(path));
|
|
12504
12734
|
if (entry.isFile() && entry.name.endsWith(".jsonl"))
|
|
@@ -12530,8 +12760,8 @@ function normalizeKey(key) {
|
|
|
12530
12760
|
var DEFAULT_PORT = 37645;
|
|
12531
12761
|
var SENSITIVE_TEXT_PATTERN6 = /https?:\/\/\S+|\b(secret|token|password|passwd|private|internal|api[_-]?key|apikey|credential|credentials|raw log|raw output|raw source|raw prompt|transcript|stdout|stderr)\b/i;
|
|
12532
12762
|
function resolveDaemonPaths(homeDir) {
|
|
12533
|
-
const rootDir =
|
|
12534
|
-
return { rootDir, lockPath:
|
|
12763
|
+
const rootDir = join19(homeDir, ".evodev", "STATE", "daemon");
|
|
12764
|
+
return { rootDir, lockPath: join19(rootDir, "lock.json"), tokenPath: join19(rootDir, "token") };
|
|
12535
12765
|
}
|
|
12536
12766
|
function validateDaemonBindHost(host) {
|
|
12537
12767
|
if (host === "127.0.0.1" || host === "localhost")
|
|
@@ -12571,14 +12801,14 @@ async function writeDaemonState(input) {
|
|
|
12571
12801
|
tokenPath: plan.paths.tokenPath,
|
|
12572
12802
|
versionText: input.versionText ?? "evodev 0.0.1-alpha"
|
|
12573
12803
|
};
|
|
12574
|
-
await
|
|
12575
|
-
await
|
|
12804
|
+
await mkdir13(dirname13(plan.paths.lockPath), { recursive: true });
|
|
12805
|
+
await writeFile12(plan.paths.tokenPath, `${token}
|
|
12576
12806
|
`, {
|
|
12577
12807
|
encoding: "utf8",
|
|
12578
12808
|
flag: "wx",
|
|
12579
12809
|
mode: 384
|
|
12580
12810
|
});
|
|
12581
|
-
await
|
|
12811
|
+
await writeFile12(plan.paths.lockPath, `${JSON.stringify(lock, null, 2)}
|
|
12582
12812
|
`, {
|
|
12583
12813
|
encoding: "utf8",
|
|
12584
12814
|
flag: "wx",
|
|
@@ -12609,8 +12839,8 @@ async function cleanupDaemonState(homeDir, token) {
|
|
|
12609
12839
|
const lock = await readDaemonLock(homeDir);
|
|
12610
12840
|
if (lock === null || lock.component !== "evodev-daemon")
|
|
12611
12841
|
throw new Error("Missing valid daemon lock.");
|
|
12612
|
-
await
|
|
12613
|
-
await
|
|
12842
|
+
await rm4(paths2.lockPath, { force: true });
|
|
12843
|
+
await rm4(paths2.tokenPath, { force: true });
|
|
12614
12844
|
return [paths2.lockPath, paths2.tokenPath];
|
|
12615
12845
|
}
|
|
12616
12846
|
async function handleDaemonRequest(input) {
|
|
@@ -12668,9 +12898,9 @@ async function handleDaemonRequest(input) {
|
|
|
12668
12898
|
if (input.path === "/memory/candidates")
|
|
12669
12899
|
return ok(await collectLearningCandidateSummaries(input.homeDir, warnings), warnings);
|
|
12670
12900
|
if (input.path === "/projects")
|
|
12671
|
-
return ok(await collectDirectorySummaries(
|
|
12901
|
+
return ok(await collectDirectorySummaries(join19(input.homeDir, ".evodev", "PROJECTS"), warnings), warnings);
|
|
12672
12902
|
if (input.path === "/packs")
|
|
12673
|
-
return ok(await collectDirectorySummaries(
|
|
12903
|
+
return ok(await collectDirectorySummaries(join19(input.homeDir, ".evodev", "PACKS"), warnings), warnings);
|
|
12674
12904
|
if (input.path === "/runs")
|
|
12675
12905
|
return ok(await collectTeamRuns(input.homeDir, warnings), warnings);
|
|
12676
12906
|
if (input.path === "/evolution/triggers")
|
|
@@ -12788,7 +13018,7 @@ function isAllowedLocalOrigin(origin) {
|
|
|
12788
13018
|
}
|
|
12789
13019
|
}
|
|
12790
13020
|
async function collectTaskSummaries(homeDir, warnings) {
|
|
12791
|
-
const root =
|
|
13021
|
+
const root = join19(homeDir, ".evodev", "STATE", "tasks");
|
|
12792
13022
|
if (!await pathExists7(root)) {
|
|
12793
13023
|
warnings.push("Task store not found; returning empty tasks.");
|
|
12794
13024
|
return [];
|
|
@@ -12826,7 +13056,7 @@ async function collectObservabilitySummaries(homeDir, warnings) {
|
|
|
12826
13056
|
}
|
|
12827
13057
|
}
|
|
12828
13058
|
async function collectLearningCandidateSummaries(homeDir, warnings) {
|
|
12829
|
-
const path =
|
|
13059
|
+
const path = join19(homeDir, ".evodev", "STATE", "learning", "candidates.jsonl");
|
|
12830
13060
|
if (!await pathExists7(path)) {
|
|
12831
13061
|
warnings.push("Learning candidate store not found; returning empty candidates.");
|
|
12832
13062
|
return [];
|
|
@@ -13063,7 +13293,7 @@ async function collectDirectorySummaries(root, warnings) {
|
|
|
13063
13293
|
warnings.push(`Store not found: ${root}`);
|
|
13064
13294
|
return [];
|
|
13065
13295
|
}
|
|
13066
|
-
const entries = await
|
|
13296
|
+
const entries = await readdir11(root, { withFileTypes: true });
|
|
13067
13297
|
return entries.filter((entry) => entry.isDirectory()).map((entry) => ({ id: entry.name, metadataOnly: true }));
|
|
13068
13298
|
}
|
|
13069
13299
|
function sanitizeMetadata(value) {
|
|
@@ -13114,13 +13344,13 @@ function optionalBodyBoolean(value) {
|
|
|
13114
13344
|
return typeof value === "boolean" ? value : undefined;
|
|
13115
13345
|
}
|
|
13116
13346
|
function resolveDaemonEvolutionProcessErrorPath(homeDir) {
|
|
13117
|
-
return
|
|
13347
|
+
return join19(resolveDaemonPaths(homeDir).rootDir, "evolution-process-error.json");
|
|
13118
13348
|
}
|
|
13119
13349
|
async function recordDaemonEvolutionProcessError(homeDir, error) {
|
|
13120
13350
|
try {
|
|
13121
13351
|
const path = resolveDaemonEvolutionProcessErrorPath(homeDir);
|
|
13122
|
-
await
|
|
13123
|
-
await
|
|
13352
|
+
await mkdir13(dirname13(path), { recursive: true });
|
|
13353
|
+
await writeFile12(path, `${JSON.stringify({
|
|
13124
13354
|
schemaVersion: 1,
|
|
13125
13355
|
kind: "daemon-evolution-process-error",
|
|
13126
13356
|
updatedAt: new Date().toISOString(),
|
|
@@ -13130,7 +13360,7 @@ async function recordDaemonEvolutionProcessError(homeDir, error) {
|
|
|
13130
13360
|
} catch {}
|
|
13131
13361
|
}
|
|
13132
13362
|
async function clearDaemonEvolutionProcessError(homeDir) {
|
|
13133
|
-
await
|
|
13363
|
+
await rm4(resolveDaemonEvolutionProcessErrorPath(homeDir), { force: true }).catch(() => {
|
|
13134
13364
|
return;
|
|
13135
13365
|
});
|
|
13136
13366
|
}
|
|
@@ -13145,10 +13375,10 @@ function describeError3(error) {
|
|
|
13145
13375
|
return error instanceof Error ? error.message : String(error);
|
|
13146
13376
|
}
|
|
13147
13377
|
async function collectNamedFiles(root, name) {
|
|
13148
|
-
const entries = await
|
|
13378
|
+
const entries = await readdir11(root, { withFileTypes: true });
|
|
13149
13379
|
const files = [];
|
|
13150
13380
|
for (const entry of entries) {
|
|
13151
|
-
const path =
|
|
13381
|
+
const path = join19(root, entry.name);
|
|
13152
13382
|
if (entry.isDirectory())
|
|
13153
13383
|
files.push(...await collectNamedFiles(path, name));
|
|
13154
13384
|
else if (entry.isFile() && entry.name === name)
|
|
@@ -13202,8 +13432,8 @@ __export(exports_review, {
|
|
|
13202
13432
|
appendLearningReviewDecision: () => appendLearningReviewDecision,
|
|
13203
13433
|
appendLearningCandidate: () => appendLearningCandidate
|
|
13204
13434
|
});
|
|
13205
|
-
import { mkdir as
|
|
13206
|
-
import { dirname as
|
|
13435
|
+
import { mkdir as mkdir14, readFile as readFile17, stat as stat10, writeFile as writeFile13 } from "node:fs/promises";
|
|
13436
|
+
import { dirname as dirname14, join as join20 } from "node:path";
|
|
13207
13437
|
var LEARNING_CANDIDATE_KINDS = [
|
|
13208
13438
|
"lesson",
|
|
13209
13439
|
"anti-criteria",
|
|
@@ -13300,7 +13530,7 @@ function createLearningCandidate(input) {
|
|
|
13300
13530
|
return candidate;
|
|
13301
13531
|
}
|
|
13302
13532
|
function validateLearningCandidate(candidate) {
|
|
13303
|
-
if (!
|
|
13533
|
+
if (!isRecord11(candidate))
|
|
13304
13534
|
throw new Error("Learning candidate must be an object.");
|
|
13305
13535
|
if (candidate.version !== 1)
|
|
13306
13536
|
throw new Error("Learning candidate version must be 1.");
|
|
@@ -13312,33 +13542,33 @@ function validateLearningCandidate(candidate) {
|
|
|
13312
13542
|
if (candidate.routingInfluence !== false) {
|
|
13313
13543
|
throw new Error("Learning candidate routingInfluence must be false in I5.");
|
|
13314
13544
|
}
|
|
13315
|
-
if (!
|
|
13545
|
+
if (!isRecord11(candidate.scope))
|
|
13316
13546
|
throw new Error("Learning candidate scope must be an object.");
|
|
13317
13547
|
assertEnumValue("scope.level", candidate.scope.level, LEARNING_SCOPE_LEVELS);
|
|
13318
|
-
if (!
|
|
13548
|
+
if (!isRecord11(candidate.content)) {
|
|
13319
13549
|
throw new Error("Learning candidate content must be an object.");
|
|
13320
13550
|
}
|
|
13321
13551
|
assertStringField("content.summary", candidate.content.summary);
|
|
13322
13552
|
assertStringField("content.howToApply", candidate.content.howToApply);
|
|
13323
13553
|
assertStringArrayField("content.antiCriteriaImpact", candidate.content.antiCriteriaImpact);
|
|
13324
|
-
if (!
|
|
13554
|
+
if (!isRecord11(candidate.provenance)) {
|
|
13325
13555
|
throw new Error("Learning candidate provenance must be an object.");
|
|
13326
13556
|
}
|
|
13327
13557
|
assertEnumValue("provenance.sourceType", candidate.provenance.sourceType, LEARNING_SOURCE_TYPES);
|
|
13328
13558
|
assertEnumValue("provenance.createdBy", candidate.provenance.createdBy, LEARNING_CREATED_BY_VALUES);
|
|
13329
13559
|
assertStringField("provenance.createdAt", candidate.provenance.createdAt);
|
|
13330
13560
|
assertStringArrayField("provenance.evidenceRefs", candidate.provenance.evidenceRefs);
|
|
13331
|
-
if (!
|
|
13561
|
+
if (!isRecord11(candidate.privacy)) {
|
|
13332
13562
|
throw new Error("Learning candidate privacy must be an object.");
|
|
13333
13563
|
}
|
|
13334
13564
|
assertEnumValue("privacy.classification", candidate.privacy.classification, ["local-private"]);
|
|
13335
|
-
if (!
|
|
13565
|
+
if (!isRecord11(candidate.review))
|
|
13336
13566
|
throw new Error("Learning candidate review must be an object.");
|
|
13337
13567
|
assertEnumValue("review.decision", candidate.review.decision, LEARNING_REVIEW_DECISIONS);
|
|
13338
13568
|
if (candidate.review.decision !== "pending") {
|
|
13339
13569
|
throw new Error("Learning candidate review decision must be pending.");
|
|
13340
13570
|
}
|
|
13341
|
-
if (!
|
|
13571
|
+
if (!isRecord11(candidate.retention)) {
|
|
13342
13572
|
throw new Error("Learning candidate retention must be an object.");
|
|
13343
13573
|
}
|
|
13344
13574
|
assertEnumValue("confidence", candidate.confidence, LEARNING_CONFIDENCE_VALUES);
|
|
@@ -13354,7 +13584,7 @@ function validateLearningCandidate(candidate) {
|
|
|
13354
13584
|
assertNoForbiddenContent2(candidate);
|
|
13355
13585
|
}
|
|
13356
13586
|
function validateLearningReviewDecisionRecord(record) {
|
|
13357
|
-
if (!
|
|
13587
|
+
if (!isRecord11(record))
|
|
13358
13588
|
throw new Error("Learning review decision must be an object.");
|
|
13359
13589
|
if (record.version !== 1)
|
|
13360
13590
|
throw new Error("Learning review decision version must be 1.");
|
|
@@ -13377,13 +13607,13 @@ function validateLearningReviewDecisionRecord(record) {
|
|
|
13377
13607
|
assertNoForbiddenContent2(record);
|
|
13378
13608
|
}
|
|
13379
13609
|
function parseLearningCandidate(value) {
|
|
13380
|
-
if (!
|
|
13610
|
+
if (!isRecord11(value))
|
|
13381
13611
|
throw new Error("Invalid learning candidate JSON.");
|
|
13382
13612
|
validateLearningCandidate(value);
|
|
13383
13613
|
return value;
|
|
13384
13614
|
}
|
|
13385
13615
|
function parseLearningReviewDecisionRecord(value) {
|
|
13386
|
-
if (!
|
|
13616
|
+
if (!isRecord11(value))
|
|
13387
13617
|
throw new Error("Invalid learning review decision JSON.");
|
|
13388
13618
|
validateLearningReviewDecisionRecord(value);
|
|
13389
13619
|
return value;
|
|
@@ -13395,16 +13625,16 @@ async function readLearningReviewDecisions(path) {
|
|
|
13395
13625
|
return parseJsonOrJsonlFile(path, parseLearningReviewDecisionRecord);
|
|
13396
13626
|
}
|
|
13397
13627
|
function resolveLearningCandidateQueuePath(homeDir) {
|
|
13398
|
-
return
|
|
13628
|
+
return join20(homeDir, ".evodev", "STATE", "learning", "candidates.jsonl");
|
|
13399
13629
|
}
|
|
13400
13630
|
function resolveLearningDecisionPath(homeDir) {
|
|
13401
|
-
return
|
|
13631
|
+
return join20(homeDir, ".evodev", "STATE", "learning", "review-decisions.jsonl");
|
|
13402
13632
|
}
|
|
13403
13633
|
async function appendLearningCandidate(homeDir, candidate) {
|
|
13404
13634
|
validateLearningCandidate(candidate);
|
|
13405
13635
|
const path = resolveLearningCandidateQueuePath(homeDir);
|
|
13406
|
-
await
|
|
13407
|
-
await
|
|
13636
|
+
await mkdir14(dirname14(path), { recursive: true });
|
|
13637
|
+
await writeFile13(path, `${JSON.stringify(candidate)}
|
|
13408
13638
|
`, { encoding: "utf8", flag: "a" });
|
|
13409
13639
|
return path;
|
|
13410
13640
|
}
|
|
@@ -13476,7 +13706,7 @@ function lintLearningCandidates(candidates2, options = {}) {
|
|
|
13476
13706
|
validateLearningReviewDecisionRecord(decision);
|
|
13477
13707
|
} catch (error) {
|
|
13478
13708
|
findings.push({
|
|
13479
|
-
candidateId:
|
|
13709
|
+
candidateId: isRecord11(decision) && typeof decision.candidateId === "string" ? decision.candidateId : "unknown",
|
|
13480
13710
|
severity: "error",
|
|
13481
13711
|
field: "decision",
|
|
13482
13712
|
message: error instanceof Error ? error.message : String(error)
|
|
@@ -13563,8 +13793,8 @@ function createLearningReviewDecisionRecord(input) {
|
|
|
13563
13793
|
async function appendLearningReviewDecision(homeDir, record) {
|
|
13564
13794
|
validateLearningReviewDecisionRecord(record);
|
|
13565
13795
|
const path = resolveLearningDecisionPath(homeDir);
|
|
13566
|
-
await
|
|
13567
|
-
await
|
|
13796
|
+
await mkdir14(dirname14(path), { recursive: true });
|
|
13797
|
+
await writeFile13(path, `${JSON.stringify(record)}
|
|
13568
13798
|
`, { encoding: "utf8", flag: "a" });
|
|
13569
13799
|
return path;
|
|
13570
13800
|
}
|
|
@@ -13674,7 +13904,7 @@ function assertNoForbiddenContent2(value) {
|
|
|
13674
13904
|
assertNoForbiddenContent2(item);
|
|
13675
13905
|
return;
|
|
13676
13906
|
}
|
|
13677
|
-
if (!
|
|
13907
|
+
if (!isRecord11(value))
|
|
13678
13908
|
return;
|
|
13679
13909
|
for (const [key, child] of Object.entries(value)) {
|
|
13680
13910
|
const normalizedKey = normalizeKey2(key);
|
|
@@ -13700,12 +13930,12 @@ function sanitizeNullableId(value) {
|
|
|
13700
13930
|
function normalizeKey2(key) {
|
|
13701
13931
|
return key.toLowerCase().replace(/[^a-z0-9]/g, "");
|
|
13702
13932
|
}
|
|
13703
|
-
function
|
|
13933
|
+
function isRecord11(value) {
|
|
13704
13934
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
13705
13935
|
}
|
|
13706
13936
|
// packages/core/src/pack/index.ts
|
|
13707
|
-
import { readFile as readFile18, readdir as
|
|
13708
|
-
import { isAbsolute as isAbsolute6, join as
|
|
13937
|
+
import { readFile as readFile18, readdir as readdir12, stat as stat11 } from "node:fs/promises";
|
|
13938
|
+
import { isAbsolute as isAbsolute6, join as join21, relative as relative7, sep } from "node:path";
|
|
13709
13939
|
|
|
13710
13940
|
// packages/core/src/protected-zones/index.ts
|
|
13711
13941
|
var SENSITIVE_DIRECTORY_SEGMENTS = new Set([
|
|
@@ -13849,7 +14079,7 @@ async function validatePack(packPath) {
|
|
|
13849
14079
|
throw new Error("Only local pack directories are supported in I6.");
|
|
13850
14080
|
}
|
|
13851
14081
|
const packRoot = await resolvePackRoot(packPath);
|
|
13852
|
-
const manifestPath =
|
|
14082
|
+
const manifestPath = join21(packRoot, MANIFEST_FILE_NAME);
|
|
13853
14083
|
const findings = [];
|
|
13854
14084
|
let manifest;
|
|
13855
14085
|
let assets = [];
|
|
@@ -13940,7 +14170,7 @@ async function planPackInstallDryRun(input) {
|
|
|
13940
14170
|
};
|
|
13941
14171
|
}
|
|
13942
14172
|
function parsePackManifest(value) {
|
|
13943
|
-
if (!
|
|
14173
|
+
if (!isRecord12(value))
|
|
13944
14174
|
throw new Error("Pack manifest must be an object.");
|
|
13945
14175
|
const manifest = value;
|
|
13946
14176
|
validateManifestTopLevelFields(manifest);
|
|
@@ -13995,7 +14225,7 @@ function parsePackManifest(value) {
|
|
|
13995
14225
|
userPath: requireString(customizations, "userPath"),
|
|
13996
14226
|
projectPath: requireString(customizations, "projectPath")
|
|
13997
14227
|
},
|
|
13998
|
-
protectedZones:
|
|
14228
|
+
protectedZones: isRecord12(manifest.protectedZones) ? {
|
|
13999
14229
|
neverInclude: optionalStringArray(manifest.protectedZones, "neverInclude"),
|
|
14000
14230
|
neverWrite: optionalStringArray(manifest.protectedZones, "neverWrite")
|
|
14001
14231
|
} : undefined,
|
|
@@ -14103,7 +14333,7 @@ function manifestAssetRefs(manifest, packRoot, findings) {
|
|
|
14103
14333
|
findings.push({ ...pathFinding, path: assetPath });
|
|
14104
14334
|
continue;
|
|
14105
14335
|
}
|
|
14106
|
-
refs.push({ kind, path: assetPath, absolutePath:
|
|
14336
|
+
refs.push({ kind, path: assetPath, absolutePath: join21(packRoot, assetPath) });
|
|
14107
14337
|
}
|
|
14108
14338
|
}
|
|
14109
14339
|
return refs;
|
|
@@ -14185,10 +14415,10 @@ function validateGuideReferences(manifest, assets, findings) {
|
|
|
14185
14415
|
}
|
|
14186
14416
|
}
|
|
14187
14417
|
async function collectPackRelativePaths(packRoot, dir = packRoot) {
|
|
14188
|
-
const entries = await
|
|
14418
|
+
const entries = await readdir12(dir, { withFileTypes: true });
|
|
14189
14419
|
const paths3 = [];
|
|
14190
14420
|
for (const entry of entries) {
|
|
14191
|
-
const absolutePath =
|
|
14421
|
+
const absolutePath = join21(dir, entry.name);
|
|
14192
14422
|
const relativePath = normalizeRelativePath(relative7(packRoot, absolutePath));
|
|
14193
14423
|
paths3.push(relativePath);
|
|
14194
14424
|
if (entry.isDirectory()) {
|
|
@@ -14219,7 +14449,7 @@ function validatePackRelativePath(path, packRoot) {
|
|
|
14219
14449
|
message: "Path must not escape pack root."
|
|
14220
14450
|
};
|
|
14221
14451
|
}
|
|
14222
|
-
const absolute =
|
|
14452
|
+
const absolute = join21(packRoot, normalized);
|
|
14223
14453
|
const rel = relative7(packRoot, absolute);
|
|
14224
14454
|
if (rel === "" || rel.startsWith("..") || isAbsolute6(rel)) {
|
|
14225
14455
|
return {
|
|
@@ -14266,7 +14496,7 @@ function parseAssets(value) {
|
|
|
14266
14496
|
function parsePermissions(value) {
|
|
14267
14497
|
if (value === undefined)
|
|
14268
14498
|
return {};
|
|
14269
|
-
if (!
|
|
14499
|
+
if (!isRecord12(value))
|
|
14270
14500
|
throw new Error("Pack manifest permissions must be an object.");
|
|
14271
14501
|
const permissions = {};
|
|
14272
14502
|
const knownKeys = new Set(Object.keys(RISKY_PERMISSION_LABELS));
|
|
@@ -14304,7 +14534,7 @@ function requireBoolean(record, key) {
|
|
|
14304
14534
|
}
|
|
14305
14535
|
function requireRecord(record, key) {
|
|
14306
14536
|
const value = record[key];
|
|
14307
|
-
if (!
|
|
14537
|
+
if (!isRecord12(value))
|
|
14308
14538
|
throw new Error(`Pack manifest missing object field: ${key}`);
|
|
14309
14539
|
return value;
|
|
14310
14540
|
}
|
|
@@ -14360,7 +14590,7 @@ function validateCompatibilityTargets(packId, targets) {
|
|
|
14360
14590
|
function validateProtectedZoneDeclarations(value) {
|
|
14361
14591
|
if (value === undefined)
|
|
14362
14592
|
return;
|
|
14363
|
-
if (!
|
|
14593
|
+
if (!isRecord12(value))
|
|
14364
14594
|
throw new Error("Pack manifest protectedZones must be an object.");
|
|
14365
14595
|
const knownFields = new Set(["neverInclude", "neverWrite"]);
|
|
14366
14596
|
for (const key of Object.keys(value)) {
|
|
@@ -14373,7 +14603,7 @@ function validateProtectedZoneDeclarations(value) {
|
|
|
14373
14603
|
function isRemotePackInput(packPath) {
|
|
14374
14604
|
return /^[a-z][a-z0-9+.-]*:\/\//i.test(packPath);
|
|
14375
14605
|
}
|
|
14376
|
-
function
|
|
14606
|
+
function isRecord12(value) {
|
|
14377
14607
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
14378
14608
|
}
|
|
14379
14609
|
function normalizeRelativePath(path) {
|
|
@@ -14386,8 +14616,8 @@ function formatError(error) {
|
|
|
14386
14616
|
return error instanceof Error ? error.message : String(error);
|
|
14387
14617
|
}
|
|
14388
14618
|
// packages/core/src/plugins/capabilities.ts
|
|
14389
|
-
import { mkdir as
|
|
14390
|
-
import { dirname as
|
|
14619
|
+
import { mkdir as mkdir15, readFile as readFile19, writeFile as writeFile14 } from "node:fs/promises";
|
|
14620
|
+
import { dirname as dirname15, join as join22 } from "node:path";
|
|
14391
14621
|
function createUnknownNegotiatedCapabilities(pluginId) {
|
|
14392
14622
|
return {
|
|
14393
14623
|
pluginId,
|
|
@@ -14497,13 +14727,13 @@ async function createCodexCapabilityVerificationArtifact(input) {
|
|
|
14497
14727
|
};
|
|
14498
14728
|
}
|
|
14499
14729
|
function resolveCodexCapabilityArtifactPath(homeDir) {
|
|
14500
|
-
return
|
|
14730
|
+
return join22(homeDir, ".evodev", "STATE", "plugins", "codex", "capability-verification.json");
|
|
14501
14731
|
}
|
|
14502
14732
|
async function writeCodexCapabilityVerificationArtifact(homeDir, artifact) {
|
|
14503
14733
|
validateCodexCapabilityVerificationArtifact(artifact);
|
|
14504
14734
|
const path = resolveCodexCapabilityArtifactPath(homeDir);
|
|
14505
|
-
await
|
|
14506
|
-
await
|
|
14735
|
+
await mkdir15(dirname15(path), { recursive: true });
|
|
14736
|
+
await writeFile14(path, `${JSON.stringify(artifact, null, 2)}
|
|
14507
14737
|
`, { encoding: "utf8", flag: "wx" });
|
|
14508
14738
|
return path;
|
|
14509
14739
|
}
|
|
@@ -14635,8 +14865,8 @@ function getEnabledPluginIds(settings) {
|
|
|
14635
14865
|
return Object.entries(settings.plugins).filter(([, pluginSettings]) => pluginSettings.enabled).map(([pluginId]) => pluginId).sort((left, right) => left.localeCompare(right));
|
|
14636
14866
|
}
|
|
14637
14867
|
// packages/core/src/project/index.ts
|
|
14638
|
-
import { mkdir as
|
|
14639
|
-
import { basename as basename5, join as
|
|
14868
|
+
import { mkdir as mkdir16, readFile as readFile20, readdir as readdir13, stat as stat12, writeFile as writeFile15 } from "node:fs/promises";
|
|
14869
|
+
import { basename as basename5, join as join23, relative as relative8 } from "node:path";
|
|
14640
14870
|
var PROJECT_CONTEXT_ALLOWED_RELATIVE_PATHS = [
|
|
14641
14871
|
".evodev/project.json",
|
|
14642
14872
|
".evodev/profile.md",
|
|
@@ -14735,7 +14965,7 @@ async function createProjectContextPlan(projectDir) {
|
|
|
14735
14965
|
sourceContentIncluded: false
|
|
14736
14966
|
};
|
|
14737
14967
|
const planFiles = await Promise.all(PROJECT_CONTEXT_ALLOWED_RELATIVE_PATHS.map(async (relativePath) => {
|
|
14738
|
-
const absolutePath =
|
|
14968
|
+
const absolutePath = join23(projectDir, relativePath);
|
|
14739
14969
|
const exists = await pathExists9(absolutePath);
|
|
14740
14970
|
return {
|
|
14741
14971
|
relativePath,
|
|
@@ -14766,9 +14996,9 @@ ${plan.errors.join(`
|
|
|
14766
14996
|
const writtenFiles = [];
|
|
14767
14997
|
for (const relativePath of PROJECT_CONTEXT_ALLOWED_RELATIVE_PATHS) {
|
|
14768
14998
|
const content = payloads[relativePath];
|
|
14769
|
-
const absolutePath =
|
|
14770
|
-
await
|
|
14771
|
-
await
|
|
14999
|
+
const absolutePath = join23(plan.projectDir, relativePath);
|
|
15000
|
+
await mkdir16(join23(absolutePath, ".."), { recursive: true });
|
|
15001
|
+
await writeFile15(absolutePath, content, { encoding: "utf8", flag: "wx" });
|
|
14772
15002
|
writtenFiles.push(relativePath);
|
|
14773
15003
|
}
|
|
14774
15004
|
return { writtenFiles };
|
|
@@ -14840,9 +15070,9 @@ function createProfileMarkdown(plan) {
|
|
|
14840
15070
|
async function collectProjectFileMetadata(projectDir) {
|
|
14841
15071
|
const files = [];
|
|
14842
15072
|
async function visit(dir) {
|
|
14843
|
-
const entries = await
|
|
15073
|
+
const entries = await readdir13(dir, { withFileTypes: true });
|
|
14844
15074
|
for (const entry of entries) {
|
|
14845
|
-
const absolutePath =
|
|
15075
|
+
const absolutePath = join23(dir, entry.name);
|
|
14846
15076
|
const relativePath = relative8(projectDir, absolutePath).replaceAll("\\", "/");
|
|
14847
15077
|
if (shouldExcludePath(relativePath, entry.isDirectory())) {
|
|
14848
15078
|
continue;
|
|
@@ -14862,7 +15092,7 @@ async function collectProjectFileMetadata(projectDir) {
|
|
|
14862
15092
|
return files.sort((left, right) => left.path.localeCompare(right.path));
|
|
14863
15093
|
}
|
|
14864
15094
|
async function collectProjectCommands(projectDir) {
|
|
14865
|
-
const packageJsonPath =
|
|
15095
|
+
const packageJsonPath = join23(projectDir, "package.json");
|
|
14866
15096
|
const warnings = [];
|
|
14867
15097
|
if (!await pathExists9(packageJsonPath)) {
|
|
14868
15098
|
return { version: 1, metadataOnly: true, rawCommandsIncluded: false, scripts: [], warnings };
|
|
@@ -14874,7 +15104,7 @@ async function collectProjectCommands(projectDir) {
|
|
|
14874
15104
|
warnings.push(`package.json scripts skipped: ${describeError4(error)}`);
|
|
14875
15105
|
return { version: 1, metadataOnly: true, rawCommandsIncluded: false, scripts: [], warnings };
|
|
14876
15106
|
}
|
|
14877
|
-
const scripts =
|
|
15107
|
+
const scripts = isRecord13(parsed) && isRecord13(parsed.scripts) ? parsed.scripts : {};
|
|
14878
15108
|
const summaries = [];
|
|
14879
15109
|
for (const [name, value] of Object.entries(scripts).sort(([left], [right]) => left.localeCompare(right))) {
|
|
14880
15110
|
if (typeof value !== "string") {
|
|
@@ -14984,14 +15214,14 @@ async function pathExists9(path) {
|
|
|
14984
15214
|
throw error;
|
|
14985
15215
|
}
|
|
14986
15216
|
}
|
|
14987
|
-
function
|
|
15217
|
+
function isRecord13(value) {
|
|
14988
15218
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
14989
15219
|
}
|
|
14990
15220
|
function describeError4(error) {
|
|
14991
15221
|
return error instanceof Error ? error.message : String(error);
|
|
14992
15222
|
}
|
|
14993
15223
|
// packages/core/src/sync/orchestrator.ts
|
|
14994
|
-
import { join as
|
|
15224
|
+
import { join as join24 } from "node:path";
|
|
14995
15225
|
async function runSync(options) {
|
|
14996
15226
|
const store = createCoreConfigStore(options.homeDir);
|
|
14997
15227
|
const settings = await store.readSettings();
|
|
@@ -15134,8 +15364,8 @@ function filterAssetsForTarget(assets, targetPlugin) {
|
|
|
15134
15364
|
}
|
|
15135
15365
|
function resolveAssetScannerPaths(assetsRootDir) {
|
|
15136
15366
|
return {
|
|
15137
|
-
skillsDir:
|
|
15138
|
-
agentsDir:
|
|
15367
|
+
skillsDir: join24(assetsRootDir, "skills"),
|
|
15368
|
+
agentsDir: join24(assetsRootDir, "agents")
|
|
15139
15369
|
};
|
|
15140
15370
|
}
|
|
15141
15371
|
async function readRegistryOrDefault(store) {
|
|
@@ -15363,7 +15593,7 @@ async function runTeamsMcpStdioServer(options = {}) {
|
|
|
15363
15593
|
}
|
|
15364
15594
|
}
|
|
15365
15595
|
function initializeResult(request) {
|
|
15366
|
-
const params =
|
|
15596
|
+
const params = isRecord14(request.params) ? request.params : {};
|
|
15367
15597
|
const requestedVersion = typeof params.protocolVersion === "string" ? params.protocolVersion : PROTOCOL_VERSION;
|
|
15368
15598
|
const protocolVersion = SUPPORTED_PROTOCOL_VERSIONS.has(requestedVersion) ? requestedVersion : PROTOCOL_VERSION;
|
|
15369
15599
|
return {
|
|
@@ -15445,7 +15675,7 @@ function toolResult(structuredContent, isError = false) {
|
|
|
15445
15675
|
};
|
|
15446
15676
|
}
|
|
15447
15677
|
function parseRequest(message) {
|
|
15448
|
-
if (!
|
|
15678
|
+
if (!isRecord14(message))
|
|
15449
15679
|
throw new Error("Invalid JSON-RPC message; expected object.");
|
|
15450
15680
|
if (message.jsonrpc !== "2.0")
|
|
15451
15681
|
throw new Error("Invalid JSON-RPC version.");
|
|
@@ -15472,7 +15702,7 @@ function jsonRpcError(id, code, message, data) {
|
|
|
15472
15702
|
return { jsonrpc: "2.0", id, error: { code, message, data } };
|
|
15473
15703
|
}
|
|
15474
15704
|
function expectRecord5(value, path) {
|
|
15475
|
-
if (!
|
|
15705
|
+
if (!isRecord14(value))
|
|
15476
15706
|
throw new Error(`Invalid ${path}; expected object.`);
|
|
15477
15707
|
return value;
|
|
15478
15708
|
}
|
|
@@ -15485,28 +15715,28 @@ function expectString3(value, path) {
|
|
|
15485
15715
|
function optionalString7(value) {
|
|
15486
15716
|
return typeof value === "string" && value.length > 0 ? value : undefined;
|
|
15487
15717
|
}
|
|
15488
|
-
function
|
|
15718
|
+
function isRecord14(value) {
|
|
15489
15719
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
15490
15720
|
}
|
|
15491
15721
|
function describeError5(error) {
|
|
15492
15722
|
return error instanceof Error ? error.message : String(error);
|
|
15493
15723
|
}
|
|
15494
15724
|
// packages/core/src/workflow/index.ts
|
|
15495
|
-
import { readFile as readFile21, readdir as
|
|
15496
|
-
import { join as
|
|
15725
|
+
import { readFile as readFile21, readdir as readdir14 } from "node:fs/promises";
|
|
15726
|
+
import { join as join25 } from "node:path";
|
|
15497
15727
|
async function scanWorkflowManifests(workflowsDir) {
|
|
15498
15728
|
const manifests = [];
|
|
15499
|
-
const entries = await
|
|
15729
|
+
const entries = await readdir14(workflowsDir, { withFileTypes: true });
|
|
15500
15730
|
for (const entry of entries) {
|
|
15501
15731
|
if (!entry.isDirectory())
|
|
15502
15732
|
continue;
|
|
15503
|
-
const manifestPath =
|
|
15733
|
+
const manifestPath = join25(workflowsDir, entry.name, "WORKFLOW.json");
|
|
15504
15734
|
manifests.push(parseWorkflowManifest(JSON.parse(await readFile21(manifestPath, "utf8"))));
|
|
15505
15735
|
}
|
|
15506
15736
|
return manifests.sort((left, right) => left.id.localeCompare(right.id));
|
|
15507
15737
|
}
|
|
15508
15738
|
function parseWorkflowManifest(value) {
|
|
15509
|
-
if (!
|
|
15739
|
+
if (!isRecord15(value))
|
|
15510
15740
|
throw new Error("Workflow manifest must be an object.");
|
|
15511
15741
|
const manifest = value;
|
|
15512
15742
|
if (typeof manifest.id !== "string" || typeof manifest.version !== "string") {
|
|
@@ -15563,24 +15793,29 @@ function formatWorkflowPlan(plan) {
|
|
|
15563
15793
|
].join(`
|
|
15564
15794
|
`);
|
|
15565
15795
|
}
|
|
15566
|
-
function
|
|
15796
|
+
function isRecord15(value) {
|
|
15567
15797
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
15568
15798
|
}
|
|
15569
15799
|
export {
|
|
15570
15800
|
writeTaskContract,
|
|
15801
|
+
writeSessionIndex,
|
|
15571
15802
|
writeProjectContext,
|
|
15803
|
+
writeJson2 as writeJson,
|
|
15572
15804
|
writeFailedOkfKnowledgePlanArtifact,
|
|
15805
|
+
writeEvolutionRepoProposal,
|
|
15573
15806
|
writeEvolutionDistillationBatch,
|
|
15574
15807
|
writeDaemonState,
|
|
15575
15808
|
writeContextInjectionReceipt,
|
|
15576
15809
|
writeCodexCapabilityVerificationArtifact,
|
|
15577
15810
|
writeCodeAgentTraceRef,
|
|
15811
|
+
withEvolutionReviewDecisionLock,
|
|
15578
15812
|
verifyTaskContract,
|
|
15579
15813
|
validatePack,
|
|
15580
15814
|
validateOkfKnowledgePlanContract,
|
|
15581
15815
|
validateObservabilityEvent,
|
|
15582
15816
|
validateLearningReviewDecisionRecord,
|
|
15583
15817
|
validateLearningCandidate,
|
|
15818
|
+
validateEvolutionRepoProposal,
|
|
15584
15819
|
validateEvolutionKnowledgeRecord,
|
|
15585
15820
|
validateEvolutionEvidenceWindow,
|
|
15586
15821
|
validateEvolutionDistillationBatch,
|
|
@@ -15591,6 +15826,7 @@ export {
|
|
|
15591
15826
|
updateTeamAgentHookState,
|
|
15592
15827
|
updateSessionMemoryFromHook,
|
|
15593
15828
|
updateSegmentTriggers,
|
|
15829
|
+
updateEvolutionRepoProposalReviewState,
|
|
15594
15830
|
updateEvolutionKnowledgeReviewState,
|
|
15595
15831
|
unsetTeamRoleBinding,
|
|
15596
15832
|
supersedeOkfKnowledgeConcept,
|
|
@@ -15607,6 +15843,7 @@ export {
|
|
|
15607
15843
|
scanSkillAssets,
|
|
15608
15844
|
scanAssets,
|
|
15609
15845
|
scanAgentAssets,
|
|
15846
|
+
sanitizeProposedChange,
|
|
15610
15847
|
runTeamsMcpStdioServer,
|
|
15611
15848
|
runSync,
|
|
15612
15849
|
runPluginConformance,
|
|
@@ -15647,14 +15884,19 @@ export {
|
|
|
15647
15884
|
readTeamAgentSummary,
|
|
15648
15885
|
readTeamAgentDefinition,
|
|
15649
15886
|
readTaskContract,
|
|
15887
|
+
readSessionState,
|
|
15888
|
+
readSessionEvidenceSegment,
|
|
15889
|
+
readSessionCursor,
|
|
15650
15890
|
readRuntimeInjectionSettings,
|
|
15651
15891
|
readPendingTeamMessagesForRole,
|
|
15652
15892
|
readOkfKnowledgeConcept,
|
|
15893
|
+
readLineRange,
|
|
15653
15894
|
readLearningReviewDecisions,
|
|
15654
15895
|
readLearningCandidates,
|
|
15655
15896
|
readFailedOkfKnowledgePlanArtifact,
|
|
15656
15897
|
readFailedOkfKnowledgePlan,
|
|
15657
15898
|
readEvolutionReviewSnapshot,
|
|
15899
|
+
readEvolutionRepoProposalById,
|
|
15658
15900
|
readEvolutionKnowledgeRecordById,
|
|
15659
15901
|
readDaemonToken,
|
|
15660
15902
|
readDaemonLock,
|
|
@@ -15700,12 +15942,14 @@ export {
|
|
|
15700
15942
|
listTeamRuns,
|
|
15701
15943
|
listTeamRoleBindings,
|
|
15702
15944
|
listTeamAgents,
|
|
15945
|
+
listSessionEvidenceSegments,
|
|
15703
15946
|
listSegmentEvolutionTriggers,
|
|
15704
15947
|
listOkfKnowledgeConcepts,
|
|
15705
15948
|
listObservabilityEvents,
|
|
15706
15949
|
listLearningReviewDecisions,
|
|
15707
15950
|
listLearningCandidates,
|
|
15708
15951
|
listEvolutionTriggers,
|
|
15952
|
+
listEvolutionKnowledgeReviewHistory,
|
|
15709
15953
|
listEvolutionKnowledgeRecords,
|
|
15710
15954
|
listEvolutionEvosCases,
|
|
15711
15955
|
listCodeAgentTraceRefs,
|
|
@@ -15717,6 +15961,7 @@ export {
|
|
|
15717
15961
|
isActiveTeamAgentStatus,
|
|
15718
15962
|
initializeCoreConfig,
|
|
15719
15963
|
hasContextInjectionReceipt,
|
|
15964
|
+
hasConcreteRepoProposalChanges,
|
|
15720
15965
|
handleTeamsMcpMessage,
|
|
15721
15966
|
handleTeamsMcpLine,
|
|
15722
15967
|
handleHookRuntime,
|
|
@@ -15784,6 +16029,7 @@ export {
|
|
|
15784
16029
|
createLexicalKnowledgeDocumentFromEvosCase,
|
|
15785
16030
|
createLearningReviewDecisionRecord,
|
|
15786
16031
|
createLearningCandidate,
|
|
16032
|
+
createEvolutionRepoProposal,
|
|
15787
16033
|
createEvolutionDistillationBatch,
|
|
15788
16034
|
createEvoDevExecutionEvent,
|
|
15789
16035
|
createDefaultTeamRuntimeSettings,
|
|
@@ -15810,6 +16056,7 @@ export {
|
|
|
15810
16056
|
assertOkfKnowledgePlanContract,
|
|
15811
16057
|
applyStrictestAgentPermissions,
|
|
15812
16058
|
appendTraceLogEntry,
|
|
16059
|
+
appendRawEvent,
|
|
15813
16060
|
appendObservabilityEvent,
|
|
15814
16061
|
appendLearningReviewDecision,
|
|
15815
16062
|
appendLearningCandidate,
|
|
@@ -15822,6 +16069,7 @@ export {
|
|
|
15822
16069
|
TmuxRuntimeAdapter,
|
|
15823
16070
|
TeamMessageBroker,
|
|
15824
16071
|
TEAM_INTERNAL_WAKE_SIGNAL,
|
|
16072
|
+
SessionMemoryEvidenceError,
|
|
15825
16073
|
PluginRegistryError,
|
|
15826
16074
|
PluginRegistry,
|
|
15827
16075
|
PROJECT_CONTEXT_ALLOWED_RELATIVE_PATHS,
|