@evo-dev/core 0.0.1-alpha.3 → 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 +164 -125
- package/dist/index.js +8965 -7632
- package/package.json +1 -1
- package/src/agents/index.ts +1 -1
- package/src/code-agent-traces/index.ts +3 -10
- package/src/config/settings.ts +14 -0
- package/src/config/store.ts +1 -1
- package/src/daemon/index.ts +1 -1
- package/src/evolution/candidates/index.ts +505 -0
- package/src/evolution/control/index.ts +19 -0
- package/src/evolution/evidence/analysis.ts +529 -0
- package/src/evolution/evidence/index.ts +3 -0
- package/src/evolution/evidence/session-memory/constants.ts +12 -0
- package/src/evolution/evidence/session-memory/index.ts +5 -0
- package/src/evolution/evidence/session-memory/paths.ts +29 -0
- package/src/evolution/evidence/session-memory/policy.ts +39 -0
- package/src/evolution/evidence/session-memory/segment.ts +192 -0
- package/src/evolution/evidence/session-memory/state-machine.ts +249 -0
- package/src/evolution/evidence/session-memory/storage.ts +266 -0
- package/src/evolution/evidence/session-memory/types.ts +207 -0
- package/src/evolution/evidence/session-memory/updater.ts +184 -0
- package/src/evolution/formatters.ts +169 -0
- package/src/evolution/index.ts +16 -2827
- package/src/{knowledge → evolution/knowledge}/index.ts +16 -81
- package/src/evolution/paths.ts +44 -0
- package/src/evolution/processor/distillation.ts +445 -0
- package/src/evolution/processor/index.ts +3 -0
- package/src/evolution/processor/process.ts +235 -0
- package/src/evolution/schema.ts +544 -0
- package/src/evolution/shared.ts +737 -0
- package/src/evolution/triggers/classification.ts +102 -0
- package/src/evolution/triggers/index.ts +295 -0
- package/src/hooks/index.ts +54 -7
- package/src/index.ts +10 -2
- package/src/runtime-logs/index.ts +4 -12
- package/src/team/index.ts +1 -1
- package/src/utils/errors.ts +13 -0
- package/src/utils/fs.ts +40 -0
- package/src/utils/hash.ts +9 -0
- package/src/utils/ids.ts +12 -0
- package/src/utils/index.ts +7 -0
- package/src/utils/parsing.ts +11 -0
- package/src/utils/text.ts +18 -0
- package/src/utils/time.ts +5 -0
- /package/src/{learning → evolution/review}/index.ts +0 -0
|
@@ -2,13 +2,13 @@ import { createHash } from "node:crypto";
|
|
|
2
2
|
import { existsSync, readFileSync, readdirSync } from "node:fs";
|
|
3
3
|
import { mkdir, readFile, readdir, rename, rm, stat, writeFile } from "node:fs/promises";
|
|
4
4
|
import { dirname, isAbsolute, join, relative, resolve } from "node:path";
|
|
5
|
-
import { resolveEvoDevPaths } from "
|
|
6
|
-
import {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
} from "../
|
|
5
|
+
import { resolveEvoDevPaths } from "../../config/paths.ts";
|
|
6
|
+
import { listEvolutionEvosCases } from "../candidates/index.ts";
|
|
7
|
+
import type {
|
|
8
|
+
EvolutionDistillationBatch,
|
|
9
|
+
EvolutionEvosCase,
|
|
10
|
+
EvolutionKnowledgeRecord,
|
|
11
|
+
} from "../schema.ts";
|
|
12
12
|
|
|
13
13
|
export type OkfKnowledgeDecision =
|
|
14
14
|
| "auto-accept"
|
|
@@ -1656,10 +1656,10 @@ export function decideOkfKnowledgeCandidate(
|
|
|
1656
1656
|
if (highRiskRequiresHuman) {
|
|
1657
1657
|
return {
|
|
1658
1658
|
...sanitized,
|
|
1659
|
-
decision: "
|
|
1659
|
+
decision: "no_write",
|
|
1660
1660
|
scores,
|
|
1661
|
-
reviewState: "
|
|
1662
|
-
decisionReason: "
|
|
1661
|
+
reviewState: "auto-stored/unreviewed",
|
|
1662
|
+
decisionReason: "No write: high-risk knowledge is not eligible for automatic activation.",
|
|
1663
1663
|
};
|
|
1664
1664
|
}
|
|
1665
1665
|
|
|
@@ -1677,12 +1677,12 @@ export function decideOkfKnowledgeCandidate(
|
|
|
1677
1677
|
if (reusable) {
|
|
1678
1678
|
return {
|
|
1679
1679
|
...sanitized,
|
|
1680
|
-
decision: "
|
|
1680
|
+
decision: "no_write",
|
|
1681
1681
|
scores,
|
|
1682
|
-
reviewState: "
|
|
1682
|
+
reviewState: "auto-stored/unreviewed",
|
|
1683
1683
|
decisionReason:
|
|
1684
1684
|
conflict?.reason ??
|
|
1685
|
-
|
|
1685
|
+
explainAutomaticNoWriteDecision(sanitized, scores, hasVerification, hasScopeTags),
|
|
1686
1686
|
};
|
|
1687
1687
|
}
|
|
1688
1688
|
|
|
@@ -1746,12 +1746,7 @@ export async function activateOkfKnowledgePlan(input: {
|
|
|
1746
1746
|
continue;
|
|
1747
1747
|
}
|
|
1748
1748
|
if (candidate.decision === "needs-human") {
|
|
1749
|
-
|
|
1750
|
-
homeDir: input.homeDir,
|
|
1751
|
-
plan: input.plan,
|
|
1752
|
-
candidate,
|
|
1753
|
-
});
|
|
1754
|
-
needsHumanCandidates.push(candidate.id);
|
|
1749
|
+
skippedCandidates.push(candidate.id);
|
|
1755
1750
|
continue;
|
|
1756
1751
|
}
|
|
1757
1752
|
if (candidate.targetStore !== "okf") {
|
|
@@ -3165,66 +3160,6 @@ function createOverlayUpdates(
|
|
|
3165
3160
|
];
|
|
3166
3161
|
}
|
|
3167
3162
|
|
|
3168
|
-
async function writeNeedsHumanKnowledgeCandidate(input: {
|
|
3169
|
-
homeDir: string;
|
|
3170
|
-
plan: OkfKnowledgePlan;
|
|
3171
|
-
candidate: OkfKnowledgePlanCandidate;
|
|
3172
|
-
}): Promise<string> {
|
|
3173
|
-
const root = join(resolveEvoDevPaths(input.homeDir).stateDir, "evolution");
|
|
3174
|
-
const targetDir = join(
|
|
3175
|
-
root,
|
|
3176
|
-
sanitizePlanStorageId("projectKey", input.plan.projectKey),
|
|
3177
|
-
sanitizePlanStorageId("runId", input.plan.runId),
|
|
3178
|
-
"review-candidates",
|
|
3179
|
-
);
|
|
3180
|
-
const targetPath = join(
|
|
3181
|
-
targetDir,
|
|
3182
|
-
`${sanitizePlanStorageId("candidateId", input.candidate.id)}.json`,
|
|
3183
|
-
);
|
|
3184
|
-
assertPathDescendant(root, targetPath, "reviewCandidatePath");
|
|
3185
|
-
const candidateSnapshot = sanitizeReviewQueueValue(input.candidate);
|
|
3186
|
-
await writeJson(
|
|
3187
|
-
targetPath,
|
|
3188
|
-
{
|
|
3189
|
-
schemaVersion: 1,
|
|
3190
|
-
kind: "evolution-review-candidate",
|
|
3191
|
-
id: input.candidate.id,
|
|
3192
|
-
projectKey: input.plan.projectKey,
|
|
3193
|
-
runId: input.plan.runId,
|
|
3194
|
-
createdAt: input.plan.createdAt,
|
|
3195
|
-
candidateKind: input.candidate.kind,
|
|
3196
|
-
title: sanitizeOkfText(input.candidate.title),
|
|
3197
|
-
targetStore: input.candidate.targetStore,
|
|
3198
|
-
targetPath: sanitizeOkfText(input.candidate.targetPath),
|
|
3199
|
-
stableKey: sanitizeOkfText(input.candidate.stableKey),
|
|
3200
|
-
reviewState: "needs-human",
|
|
3201
|
-
reasons: [sanitizeOkfText(input.candidate.decisionReason)],
|
|
3202
|
-
candidate: candidateSnapshot,
|
|
3203
|
-
provenance: {
|
|
3204
|
-
runId: input.plan.runId,
|
|
3205
|
-
evidenceWindowId: input.plan.evidenceWindowId,
|
|
3206
|
-
evidenceRefs: input.candidate.evidenceRefs.map(sanitizeOkfText),
|
|
3207
|
-
createdBy: "evodev",
|
|
3208
|
-
rawLogsStored: false,
|
|
3209
|
-
rawPromptsStored: false,
|
|
3210
|
-
sourceDumpsStored: false,
|
|
3211
|
-
rawCommandOutputStored: false,
|
|
3212
|
-
},
|
|
3213
|
-
privacy: {
|
|
3214
|
-
classification: "local-private",
|
|
3215
|
-
rawPromptsStored: false,
|
|
3216
|
-
rawLogsStored: false,
|
|
3217
|
-
sourceDumpsStored: false,
|
|
3218
|
-
rawCommandOutputStored: false,
|
|
3219
|
-
secretsStored: false,
|
|
3220
|
-
internalLinksStored: false,
|
|
3221
|
-
},
|
|
3222
|
-
},
|
|
3223
|
-
{ overwrite: true },
|
|
3224
|
-
);
|
|
3225
|
-
return targetPath;
|
|
3226
|
-
}
|
|
3227
|
-
|
|
3228
3163
|
function sanitizeOkfPlanCandidate(candidate: OkfKnowledgePlanCandidate): OkfKnowledgePlanCandidate {
|
|
3229
3164
|
return sanitizeReviewQueueValue(candidate) as OkfKnowledgePlanCandidate;
|
|
3230
3165
|
}
|
|
@@ -3292,7 +3227,7 @@ function hasHighRiskHumanReviewSignal(candidate: unknown): boolean {
|
|
|
3292
3227
|
return HUMAN_REVIEW_DOMAIN_PATTERN.test(values.join(" "));
|
|
3293
3228
|
}
|
|
3294
3229
|
|
|
3295
|
-
function
|
|
3230
|
+
function explainAutomaticNoWriteDecision(
|
|
3296
3231
|
candidate: OkfKnowledgePlanCandidate,
|
|
3297
3232
|
scores: OkfKnowledgeCandidateScores,
|
|
3298
3233
|
hasVerification: boolean,
|
|
@@ -3308,7 +3243,7 @@ function explainNeedsHumanDecision(
|
|
|
3308
3243
|
if (scores.privacyRisk > 2) reasons.push("privacy risk is above auto-accept");
|
|
3309
3244
|
if (scores.stability < 3) reasons.push("stability is below auto-accept");
|
|
3310
3245
|
if (scores.duplicationRisk > 2) reasons.push("duplication risk is above auto-accept");
|
|
3311
|
-
return `
|
|
3246
|
+
return `No write: ${reasons.join("; ") || "auto-accept gates were not all satisfied"}.`;
|
|
3312
3247
|
}
|
|
3313
3248
|
|
|
3314
3249
|
function resolveCandidateReviewStateForWrite(
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { join } from "node:path";
|
|
2
|
+
import { resolveEvoDevPaths } from "../config/paths.ts";
|
|
3
|
+
import type { EvolutionResolvedPaths } from "./schema.ts";
|
|
4
|
+
import { assertPathDescendant, sanitizeStorageId } from "./shared.ts";
|
|
5
|
+
|
|
6
|
+
export function resolveEvolutionPaths(input: {
|
|
7
|
+
homeDir: string;
|
|
8
|
+
projectKey: string;
|
|
9
|
+
runId: string;
|
|
10
|
+
}): EvolutionResolvedPaths {
|
|
11
|
+
const paths = resolveEvoDevPaths(input.homeDir);
|
|
12
|
+
const projectKey = sanitizeStorageId("projectKey", input.projectKey);
|
|
13
|
+
const runId = sanitizeStorageId("runId", input.runId);
|
|
14
|
+
const evolutionStateDir = join(paths.stateDir, "evolution");
|
|
15
|
+
const projectStateDir = join(evolutionStateDir, projectKey);
|
|
16
|
+
const runStateDir = join(evolutionStateDir, projectKey, runId);
|
|
17
|
+
const repoProposalsDir = join(runStateDir, "proposals");
|
|
18
|
+
const reviewCandidatesDir = join(runStateDir, "review-candidates");
|
|
19
|
+
const knowledgeProjectDir = join(paths.knowledgeDir, projectKey);
|
|
20
|
+
const evosCasesProjectDir = join(paths.evosCasesDir, projectKey);
|
|
21
|
+
assertPathDescendant(evolutionStateDir, runStateDir, "runStateDir");
|
|
22
|
+
assertPathDescendant(evolutionStateDir, projectStateDir, "projectStateDir");
|
|
23
|
+
assertPathDescendant(evolutionStateDir, reviewCandidatesDir, "reviewCandidatesDir");
|
|
24
|
+
assertPathDescendant(paths.knowledgeDir, knowledgeProjectDir, "knowledgeProjectDir");
|
|
25
|
+
assertPathDescendant(paths.evosCasesDir, evosCasesProjectDir, "evosCasesProjectDir");
|
|
26
|
+
return {
|
|
27
|
+
projectKey,
|
|
28
|
+
runId,
|
|
29
|
+
evolutionStateDir,
|
|
30
|
+
runStateDir,
|
|
31
|
+
evidenceWindowPath: join(runStateDir, "evidence-window.json"),
|
|
32
|
+
batchPath: join(runStateDir, "distillation-batch.json"),
|
|
33
|
+
triggersDir: join(runStateDir, "triggers"),
|
|
34
|
+
reviewCandidatesDir,
|
|
35
|
+
repoProposalsDir,
|
|
36
|
+
repoProposalsIndexPath: join(runStateDir, "proposals.index.json"),
|
|
37
|
+
knowledgeProjectDir,
|
|
38
|
+
knowledgeRecordsDir: join(knowledgeProjectDir, "records"),
|
|
39
|
+
knowledgeIndexPath: join(knowledgeProjectDir, "index.json"),
|
|
40
|
+
knowledgeReviewHistoryPath: join(projectStateDir, "knowledge-review-history.jsonl"),
|
|
41
|
+
evosCasesProjectDir,
|
|
42
|
+
evosIndexPath: paths.evosIndexPath,
|
|
43
|
+
};
|
|
44
|
+
}
|
|
@@ -0,0 +1,445 @@
|
|
|
1
|
+
import { readFile, readdir } from "node:fs/promises";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { resolveEvoDevPaths } from "../../config/paths.ts";
|
|
4
|
+
import {
|
|
5
|
+
createStableId,
|
|
6
|
+
normalizeTimestamp,
|
|
7
|
+
readJsonFiles,
|
|
8
|
+
writeJsonFile as writeJson,
|
|
9
|
+
} from "../../utils/index.ts";
|
|
10
|
+
import {
|
|
11
|
+
activateOkfKnowledgePlan,
|
|
12
|
+
createOkfKnowledgePlanFromDistillationBatch,
|
|
13
|
+
} from "../knowledge/index.ts";
|
|
14
|
+
import { resolveEvolutionPaths } from "../paths.ts";
|
|
15
|
+
import type {
|
|
16
|
+
EvolutionActivationResult,
|
|
17
|
+
EvolutionConfidence,
|
|
18
|
+
EvolutionDistillationBatch,
|
|
19
|
+
EvolutionEvidenceWindow,
|
|
20
|
+
EvolutionEvosCase,
|
|
21
|
+
EvolutionKnowledgeRecord,
|
|
22
|
+
EvolutionRepoProposal,
|
|
23
|
+
EvolutionWriteResult,
|
|
24
|
+
} from "../schema.ts";
|
|
25
|
+
import {
|
|
26
|
+
collectRoleIds,
|
|
27
|
+
createEvolutionEvosCase,
|
|
28
|
+
createEvolutionKnowledgeRecord,
|
|
29
|
+
createPrivacyFields,
|
|
30
|
+
hasConcreteRepoProposalChanges,
|
|
31
|
+
listDirectoryNames,
|
|
32
|
+
parseEvosCase,
|
|
33
|
+
parseKnowledgeRecord,
|
|
34
|
+
parseRepoProposal,
|
|
35
|
+
sanitizeText,
|
|
36
|
+
validateEvolutionDistillationBatch,
|
|
37
|
+
validateEvolutionEvidenceWindow,
|
|
38
|
+
validateEvolutionEvosCase,
|
|
39
|
+
validateEvolutionRepoProposal,
|
|
40
|
+
} from "../shared.ts";
|
|
41
|
+
import { chooseEvosTriggerKind, createTriggerSummary } from "../triggers/classification.ts";
|
|
42
|
+
|
|
43
|
+
export function createEvolutionDistillationBatch(input: {
|
|
44
|
+
evidenceWindow: EvolutionEvidenceWindow;
|
|
45
|
+
now?: string | Date;
|
|
46
|
+
warnings?: string[];
|
|
47
|
+
}): EvolutionDistillationBatch {
|
|
48
|
+
validateEvolutionEvidenceWindow(input.evidenceWindow);
|
|
49
|
+
const createdAt = normalizeTimestamp(input.now);
|
|
50
|
+
const roleIds = collectRoleIds(input.evidenceWindow);
|
|
51
|
+
const sourceRefs = input.evidenceWindow.sourceRefs.map((sourceRef) => sourceRef.id);
|
|
52
|
+
const failedSignals = input.evidenceWindow.events.filter(
|
|
53
|
+
(event) =>
|
|
54
|
+
event.kind === "error" ||
|
|
55
|
+
event.triggerReason === "stop-failure" ||
|
|
56
|
+
event.triggerReason === "tool-failure" ||
|
|
57
|
+
event.triggerReason === "permission-denied" ||
|
|
58
|
+
/fail|failed|failure|error|issue/i.test(event.summary),
|
|
59
|
+
);
|
|
60
|
+
const verificationSignals = input.evidenceWindow.events
|
|
61
|
+
.filter(
|
|
62
|
+
(event) => event.kind === "verification" || /test|lint|typecheck|build/i.test(event.summary),
|
|
63
|
+
)
|
|
64
|
+
.slice(0, 8)
|
|
65
|
+
.map((event) => event.summary);
|
|
66
|
+
const autoKnowledgeEligible =
|
|
67
|
+
input.evidenceWindow.events.length > 0 &&
|
|
68
|
+
failedSignals.length === 0 &&
|
|
69
|
+
verificationSignals.length > 0 &&
|
|
70
|
+
input.evidenceWindow.triggerPolicy.distillRecommended &&
|
|
71
|
+
input.evidenceWindow.triggerPolicy.evidenceSignals.failures === 0;
|
|
72
|
+
const confidence: EvolutionConfidence = autoKnowledgeEligible
|
|
73
|
+
? "high"
|
|
74
|
+
: failedSignals.length === 0
|
|
75
|
+
? "medium"
|
|
76
|
+
: "low";
|
|
77
|
+
|
|
78
|
+
const knowledgeRecords = !autoKnowledgeEligible
|
|
79
|
+
? []
|
|
80
|
+
: [
|
|
81
|
+
createEvolutionKnowledgeRecord({
|
|
82
|
+
id: createStableId("k", [input.evidenceWindow.id, "run-summary"]),
|
|
83
|
+
kind: "run-summary",
|
|
84
|
+
title: `Project run ${input.evidenceWindow.runId} evidence summary`,
|
|
85
|
+
projectKey: input.evidenceWindow.projectKey,
|
|
86
|
+
summary: createRunSummary(input.evidenceWindow, roleIds, failedSignals.length),
|
|
87
|
+
body: [
|
|
88
|
+
`Run ${input.evidenceWindow.runId} produced ${input.evidenceWindow.events.length} redacted metadata evidence event(s).`,
|
|
89
|
+
`Observed role(s): ${roleIds.length === 0 ? "none" : roleIds.join(", ")}.`,
|
|
90
|
+
"This record is derived from metadata-only evidence; active use depends on OKF plan review state.",
|
|
91
|
+
].join("\n"),
|
|
92
|
+
roleTags: roleIds,
|
|
93
|
+
tags: ["project-run", "auto-stored", "unreviewed"],
|
|
94
|
+
reviewState: "auto-stored/unreviewed",
|
|
95
|
+
authority: "contextual",
|
|
96
|
+
confidence,
|
|
97
|
+
provenance: createProvenance(input.evidenceWindow, createdAt, sourceRefs),
|
|
98
|
+
relations: {
|
|
99
|
+
relatedKnowledgeIds: [],
|
|
100
|
+
evosCaseIds: [createStableId("evo", [input.evidenceWindow.id, "case"])],
|
|
101
|
+
proposalIds: [],
|
|
102
|
+
supersedes: [],
|
|
103
|
+
},
|
|
104
|
+
privacy: createPrivacyFields(),
|
|
105
|
+
runtime: { canLoad: true, hardBlocking: false },
|
|
106
|
+
}),
|
|
107
|
+
];
|
|
108
|
+
|
|
109
|
+
const evosCases =
|
|
110
|
+
input.evidenceWindow.events.length === 0
|
|
111
|
+
? []
|
|
112
|
+
: [
|
|
113
|
+
createEvolutionEvosCase({
|
|
114
|
+
id: createStableId("evo", [input.evidenceWindow.id, "case"]),
|
|
115
|
+
projectKey: input.evidenceWindow.projectKey,
|
|
116
|
+
title: `Run ${input.evidenceWindow.runId} evolution case`,
|
|
117
|
+
roleTags: roleIds,
|
|
118
|
+
tags: [
|
|
119
|
+
"project-run",
|
|
120
|
+
"evidence-window",
|
|
121
|
+
`trigger-${input.evidenceWindow.triggerPolicy.strongest}`,
|
|
122
|
+
],
|
|
123
|
+
reviewState: "auto-stored/unreviewed",
|
|
124
|
+
confidence,
|
|
125
|
+
trigger: {
|
|
126
|
+
kind: chooseEvosTriggerKind(input.evidenceWindow),
|
|
127
|
+
summary: createTriggerSummary(input.evidenceWindow),
|
|
128
|
+
},
|
|
129
|
+
intervention: {
|
|
130
|
+
summary: `Collected ${input.evidenceWindow.events.length} metadata event(s) and ${input.evidenceWindow.episodes.length} episode(s) from EvoDev execution logs.`,
|
|
131
|
+
roleIds,
|
|
132
|
+
},
|
|
133
|
+
result: {
|
|
134
|
+
summary:
|
|
135
|
+
failedSignals.length === 0
|
|
136
|
+
? "No failure signal was detected in the redacted evidence summaries."
|
|
137
|
+
: `${failedSignals.length} potential failure or issue signal(s) were detected.`,
|
|
138
|
+
verificationSignals,
|
|
139
|
+
},
|
|
140
|
+
expectedFutureBehavior:
|
|
141
|
+
"Future role agents should inspect accepted knowledge first and treat unreviewed cases as contextual evidence only.",
|
|
142
|
+
provenance: createProvenance(input.evidenceWindow, createdAt, sourceRefs),
|
|
143
|
+
privacy: createPrivacyFields(),
|
|
144
|
+
}),
|
|
145
|
+
];
|
|
146
|
+
|
|
147
|
+
// Failure metadata is evidence, not a repository change proposal. A proposal is only valid
|
|
148
|
+
// after another stage supplies concrete target files and proposed changes.
|
|
149
|
+
const repoProposals: EvolutionRepoProposal[] = [];
|
|
150
|
+
|
|
151
|
+
const batch: EvolutionDistillationBatch = {
|
|
152
|
+
schemaVersion: 1,
|
|
153
|
+
id: createStableId("batch", [input.evidenceWindow.id, createdAt]),
|
|
154
|
+
projectKey: input.evidenceWindow.projectKey,
|
|
155
|
+
runId: input.evidenceWindow.runId,
|
|
156
|
+
createdAt,
|
|
157
|
+
evidenceWindow: input.evidenceWindow,
|
|
158
|
+
knowledgeRecords,
|
|
159
|
+
evosCases,
|
|
160
|
+
repoProposals,
|
|
161
|
+
warnings: (input.warnings ?? []).map(sanitizeText),
|
|
162
|
+
};
|
|
163
|
+
validateEvolutionDistillationBatch(batch);
|
|
164
|
+
return batch;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
export async function writeEvolutionDistillationBatch(input: {
|
|
168
|
+
homeDir: string;
|
|
169
|
+
batch: EvolutionDistillationBatch;
|
|
170
|
+
overwrite?: boolean;
|
|
171
|
+
}): Promise<EvolutionWriteResult> {
|
|
172
|
+
validateEvolutionDistillationBatch(input.batch);
|
|
173
|
+
const paths = resolveEvolutionPaths({
|
|
174
|
+
homeDir: input.homeDir,
|
|
175
|
+
projectKey: input.batch.projectKey,
|
|
176
|
+
runId: input.batch.runId,
|
|
177
|
+
});
|
|
178
|
+
const overwrite = input.overwrite === true;
|
|
179
|
+
await writeJson(paths.evidenceWindowPath, input.batch.evidenceWindow, { overwrite });
|
|
180
|
+
await writeJson(paths.batchPath, input.batch, { overwrite });
|
|
181
|
+
|
|
182
|
+
const knowledgePaths: string[] = [];
|
|
183
|
+
for (const record of input.batch.knowledgeRecords) {
|
|
184
|
+
const path = join(paths.knowledgeRecordsDir, `${record.id}.json`);
|
|
185
|
+
await writeJson(path, record, { overwrite });
|
|
186
|
+
knowledgePaths.push(path);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
const evosCasePaths: string[] = [];
|
|
190
|
+
for (const evosCase of input.batch.evosCases) {
|
|
191
|
+
const path = join(paths.evosCasesProjectDir, `${evosCase.id}.json`);
|
|
192
|
+
await writeJson(path, evosCase, { overwrite });
|
|
193
|
+
evosCasePaths.push(path);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
const repoProposalPaths: string[] = [];
|
|
197
|
+
for (const proposal of input.batch.repoProposals) {
|
|
198
|
+
const path = join(paths.repoProposalsDir, `${proposal.id}.json`);
|
|
199
|
+
await writeJson(path, proposal, { overwrite });
|
|
200
|
+
repoProposalPaths.push(path);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
const allProjectKnowledgeRecords = await readJsonFiles(
|
|
204
|
+
paths.knowledgeRecordsDir,
|
|
205
|
+
parseKnowledgeRecord,
|
|
206
|
+
);
|
|
207
|
+
await writeJson(
|
|
208
|
+
paths.knowledgeIndexPath,
|
|
209
|
+
{
|
|
210
|
+
schemaVersion: 1,
|
|
211
|
+
projectKey: input.batch.projectKey,
|
|
212
|
+
updatedAt: input.batch.createdAt,
|
|
213
|
+
records: allProjectKnowledgeRecords.map((record) => ({
|
|
214
|
+
id: record.id,
|
|
215
|
+
kind: record.kind,
|
|
216
|
+
title: record.title,
|
|
217
|
+
reviewState: record.reviewState,
|
|
218
|
+
roleTags: record.roleTags,
|
|
219
|
+
})),
|
|
220
|
+
},
|
|
221
|
+
{ overwrite: true },
|
|
222
|
+
);
|
|
223
|
+
|
|
224
|
+
const evosCasesRootDir = resolveEvoDevPaths(input.homeDir).evosCasesDir;
|
|
225
|
+
const evosProjectKeys = await listDirectoryNames(evosCasesRootDir);
|
|
226
|
+
const evosProjects = await Promise.all(
|
|
227
|
+
evosProjectKeys.map(async (projectKey) => {
|
|
228
|
+
const cases = await readJsonFiles(join(evosCasesRootDir, projectKey), parseEvosCase);
|
|
229
|
+
return {
|
|
230
|
+
projectKey,
|
|
231
|
+
cases: cases.map((evosCase) => ({
|
|
232
|
+
id: evosCase.id,
|
|
233
|
+
title: evosCase.title,
|
|
234
|
+
reviewState: evosCase.reviewState,
|
|
235
|
+
})),
|
|
236
|
+
};
|
|
237
|
+
}),
|
|
238
|
+
);
|
|
239
|
+
await writeJson(
|
|
240
|
+
paths.evosIndexPath,
|
|
241
|
+
{
|
|
242
|
+
schemaVersion: 1,
|
|
243
|
+
updatedAt: input.batch.createdAt,
|
|
244
|
+
projects: evosProjects,
|
|
245
|
+
},
|
|
246
|
+
{ overwrite: true },
|
|
247
|
+
);
|
|
248
|
+
|
|
249
|
+
const allRunProposals = await readJsonFiles(paths.repoProposalsDir, parseRepoProposal);
|
|
250
|
+
await writeJson(
|
|
251
|
+
paths.repoProposalsIndexPath,
|
|
252
|
+
{
|
|
253
|
+
schemaVersion: 1,
|
|
254
|
+
projectKey: input.batch.projectKey,
|
|
255
|
+
runId: input.batch.runId,
|
|
256
|
+
updatedAt: input.batch.createdAt,
|
|
257
|
+
proposals: allRunProposals.map((proposal) => ({
|
|
258
|
+
id: proposal.id,
|
|
259
|
+
kind: proposal.kind,
|
|
260
|
+
title: proposal.title,
|
|
261
|
+
reviewState: proposal.reviewState,
|
|
262
|
+
})),
|
|
263
|
+
},
|
|
264
|
+
{ overwrite: true },
|
|
265
|
+
);
|
|
266
|
+
|
|
267
|
+
return {
|
|
268
|
+
evidenceWindowPath: paths.evidenceWindowPath,
|
|
269
|
+
batchPath: paths.batchPath,
|
|
270
|
+
knowledgePaths,
|
|
271
|
+
evosCasePaths,
|
|
272
|
+
repoProposalPaths,
|
|
273
|
+
knowledgeIndexPath: paths.knowledgeIndexPath,
|
|
274
|
+
evosIndexPath: paths.evosIndexPath,
|
|
275
|
+
};
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
export async function writeEvolutionRepoProposal(input: {
|
|
279
|
+
homeDir: string;
|
|
280
|
+
proposal: EvolutionRepoProposal;
|
|
281
|
+
}): Promise<{ proposalPath: string; indexPath: string }> {
|
|
282
|
+
validateEvolutionRepoProposal(input.proposal);
|
|
283
|
+
if (!hasConcreteRepoProposalChanges(input.proposal)) {
|
|
284
|
+
throw new Error("Repo proposal must include a target repository and concrete file changes.");
|
|
285
|
+
}
|
|
286
|
+
const paths = resolveEvolutionPaths({
|
|
287
|
+
homeDir: input.homeDir,
|
|
288
|
+
projectKey: input.proposal.projectKey,
|
|
289
|
+
runId: input.proposal.provenance.runId,
|
|
290
|
+
});
|
|
291
|
+
const proposalPath = join(paths.repoProposalsDir, `${input.proposal.id}.json`);
|
|
292
|
+
await writeJson(proposalPath, input.proposal, { overwrite: false });
|
|
293
|
+
|
|
294
|
+
const allRunProposals = await readJsonFiles(paths.repoProposalsDir, parseRepoProposal);
|
|
295
|
+
await writeJson(
|
|
296
|
+
paths.repoProposalsIndexPath,
|
|
297
|
+
{
|
|
298
|
+
schemaVersion: 1,
|
|
299
|
+
projectKey: input.proposal.projectKey,
|
|
300
|
+
runId: input.proposal.provenance.runId,
|
|
301
|
+
updatedAt: input.proposal.provenance.createdAt,
|
|
302
|
+
proposals: allRunProposals.map((proposal) => ({
|
|
303
|
+
id: proposal.id,
|
|
304
|
+
kind: proposal.kind,
|
|
305
|
+
title: proposal.title,
|
|
306
|
+
reviewState: proposal.reviewState,
|
|
307
|
+
})),
|
|
308
|
+
},
|
|
309
|
+
{ overwrite: true },
|
|
310
|
+
);
|
|
311
|
+
return { proposalPath, indexPath: paths.repoProposalsIndexPath };
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
export async function activateEvolutionDistillationBatch(input: {
|
|
315
|
+
homeDir: string;
|
|
316
|
+
batch: EvolutionDistillationBatch;
|
|
317
|
+
overwrite?: boolean;
|
|
318
|
+
}): Promise<EvolutionActivationResult> {
|
|
319
|
+
validateEvolutionDistillationBatch(input.batch);
|
|
320
|
+
const paths = resolveEvolutionPaths({
|
|
321
|
+
homeDir: input.homeDir,
|
|
322
|
+
projectKey: input.batch.projectKey,
|
|
323
|
+
runId: input.batch.runId,
|
|
324
|
+
});
|
|
325
|
+
const overwrite = input.overwrite === true;
|
|
326
|
+
await writeJson(paths.evidenceWindowPath, input.batch.evidenceWindow, { overwrite });
|
|
327
|
+
await writeJson(paths.batchPath, input.batch, { overwrite });
|
|
328
|
+
|
|
329
|
+
const plan = createOkfKnowledgePlanFromDistillationBatch(input.batch, {
|
|
330
|
+
homeDir: input.homeDir,
|
|
331
|
+
});
|
|
332
|
+
const okf = await activateOkfKnowledgePlan({
|
|
333
|
+
homeDir: input.homeDir,
|
|
334
|
+
plan,
|
|
335
|
+
overwrite,
|
|
336
|
+
evidenceWindowPath: paths.evidenceWindowPath,
|
|
337
|
+
});
|
|
338
|
+
|
|
339
|
+
const evosCasePaths: string[] = [];
|
|
340
|
+
for (const evosCase of input.batch.evosCases) {
|
|
341
|
+
const matchingCandidate = plan.candidates.find((candidate) => candidate.id === evosCase.id);
|
|
342
|
+
const persistedEvosCase =
|
|
343
|
+
matchingCandidate?.decision === "auto-accept"
|
|
344
|
+
? { ...evosCase, reviewState: "auto-accepted" as const }
|
|
345
|
+
: matchingCandidate?.decision === "needs-human"
|
|
346
|
+
? { ...evosCase, reviewState: "needs-human" as const }
|
|
347
|
+
: evosCase;
|
|
348
|
+
validateEvolutionEvosCase(persistedEvosCase);
|
|
349
|
+
const path = join(paths.evosCasesProjectDir, `${evosCase.id}.json`);
|
|
350
|
+
await writeJson(path, persistedEvosCase, { overwrite });
|
|
351
|
+
evosCasePaths.push(path);
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
const repoProposalPaths: string[] = [];
|
|
355
|
+
for (const proposal of input.batch.repoProposals) {
|
|
356
|
+
const path = join(paths.repoProposalsDir, `${proposal.id}.json`);
|
|
357
|
+
await writeJson(path, proposal, { overwrite });
|
|
358
|
+
repoProposalPaths.push(path);
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
const evosCasesRootDir = resolveEvoDevPaths(input.homeDir).evosCasesDir;
|
|
362
|
+
const evosProjectKeys = await listDirectoryNames(evosCasesRootDir);
|
|
363
|
+
const evosProjects = await Promise.all(
|
|
364
|
+
evosProjectKeys.map(async (projectKey) => {
|
|
365
|
+
const cases = await readJsonFiles(join(evosCasesRootDir, projectKey), parseEvosCase);
|
|
366
|
+
return {
|
|
367
|
+
projectKey,
|
|
368
|
+
cases: cases.map((evosCase) => ({
|
|
369
|
+
id: evosCase.id,
|
|
370
|
+
title: evosCase.title,
|
|
371
|
+
reviewState: evosCase.reviewState,
|
|
372
|
+
})),
|
|
373
|
+
};
|
|
374
|
+
}),
|
|
375
|
+
);
|
|
376
|
+
await writeJson(
|
|
377
|
+
paths.evosIndexPath,
|
|
378
|
+
{
|
|
379
|
+
schemaVersion: 1,
|
|
380
|
+
updatedAt: input.batch.createdAt,
|
|
381
|
+
projects: evosProjects,
|
|
382
|
+
},
|
|
383
|
+
{ overwrite: true },
|
|
384
|
+
);
|
|
385
|
+
|
|
386
|
+
const allRunProposals = await readJsonFiles(paths.repoProposalsDir, parseRepoProposal);
|
|
387
|
+
await writeJson(
|
|
388
|
+
paths.repoProposalsIndexPath,
|
|
389
|
+
{
|
|
390
|
+
schemaVersion: 1,
|
|
391
|
+
projectKey: input.batch.projectKey,
|
|
392
|
+
runId: input.batch.runId,
|
|
393
|
+
updatedAt: input.batch.createdAt,
|
|
394
|
+
proposals: allRunProposals.map((proposal) => ({
|
|
395
|
+
id: proposal.id,
|
|
396
|
+
kind: proposal.kind,
|
|
397
|
+
title: proposal.title,
|
|
398
|
+
reviewState: proposal.reviewState,
|
|
399
|
+
})),
|
|
400
|
+
},
|
|
401
|
+
{ overwrite: true },
|
|
402
|
+
);
|
|
403
|
+
|
|
404
|
+
return {
|
|
405
|
+
evidenceWindowPath: paths.evidenceWindowPath,
|
|
406
|
+
batchPath: paths.batchPath,
|
|
407
|
+
okf,
|
|
408
|
+
evosCasePaths,
|
|
409
|
+
repoProposalPaths,
|
|
410
|
+
evosIndexPath: paths.evosIndexPath,
|
|
411
|
+
};
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
function createRunSummary(
|
|
415
|
+
evidenceWindow: EvolutionEvidenceWindow,
|
|
416
|
+
roleIds: string[],
|
|
417
|
+
failedSignalCount: number,
|
|
418
|
+
): string {
|
|
419
|
+
const failureClause =
|
|
420
|
+
failedSignalCount === 0
|
|
421
|
+
? "No failure signal was detected in metadata summaries."
|
|
422
|
+
: `${failedSignalCount} potential failure or issue signal(s) were detected.`;
|
|
423
|
+
return sanitizeText(
|
|
424
|
+
`Run ${evidenceWindow.runId} produced ${evidenceWindow.events.length} redacted event(s) across ${roleIds.length} role(s). ${failureClause}`,
|
|
425
|
+
);
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
function createProvenance(
|
|
429
|
+
evidenceWindow: EvolutionEvidenceWindow,
|
|
430
|
+
createdAt: string,
|
|
431
|
+
sourceRefs: string[],
|
|
432
|
+
): EvolutionKnowledgeRecord["provenance"] {
|
|
433
|
+
return {
|
|
434
|
+
runId: evidenceWindow.runId,
|
|
435
|
+
taskId: evidenceWindow.taskId,
|
|
436
|
+
evidenceWindowId: evidenceWindow.id,
|
|
437
|
+
sourceRefs,
|
|
438
|
+
createdAt,
|
|
439
|
+
createdBy: "evodev",
|
|
440
|
+
rawLogsStored: false,
|
|
441
|
+
rawPromptsStored: false,
|
|
442
|
+
sourceDumpsStored: false,
|
|
443
|
+
rawCommandOutputStored: false,
|
|
444
|
+
};
|
|
445
|
+
}
|