@evo-dev/core 0.0.1-alpha.1 → 0.0.1-alpha.3
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/assets/skills/coding/knowledge-distillation/SKILL.md +112 -111
- package/assets/skills/coding/knowledge-distillation/references/knowledge-distillation-methods.md +11 -7
- package/assets/team/agents/code-reviewer.md +48 -0
- package/assets/team/agents/docs-maintainer.md +51 -0
- package/assets/team/agents/implementation-engineer.md +51 -0
- package/assets/team/agents/product-scope-analyst.md +58 -0
- package/assets/team/agents/release-engineer.md +55 -0
- package/assets/team/agents/security-boundary-reviewer.md +50 -0
- package/assets/team/agents/solution-architect.md +51 -0
- package/assets/team/agents/verification-engineer.md +51 -0
- package/assets/team/team.md +102 -0
- package/dist/config/index.js +774 -34
- package/dist/index.js +7331 -1573
- package/package.json +5 -1
- package/src/agents/index.ts +56 -0
- package/src/code-agent-traces/index.ts +521 -0
- package/src/config/index.ts +3 -0
- package/src/config/paths.ts +1 -1
- package/src/config/settings.ts +78 -0
- package/src/config/store.ts +2 -0
- package/src/daemon/index.ts +98 -9
- package/src/evolution/index.ts +494 -23
- package/src/hooks/index.ts +315 -36
- package/src/index.ts +2 -0
- package/src/knowledge/index.ts +4784 -0
- package/src/runtime-logs/index.ts +490 -16
- package/src/team/index.ts +1429 -185
- package/src/team/mcp.ts +9 -5
- package/src/team/prompts.ts +141 -0
package/src/evolution/index.ts
CHANGED
|
@@ -1,16 +1,24 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
2
|
import { mkdir, readFile, readdir, rm, stat, writeFile } from "node:fs/promises";
|
|
3
3
|
import { basename, dirname, isAbsolute, join, relative, resolve } from "node:path";
|
|
4
|
+
import { readCodeAgentTraceRef } from "../code-agent-traces/index.ts";
|
|
4
5
|
import { resolveEvoDevPaths } from "../config/paths.ts";
|
|
5
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
type OkfKnowledgeActivationResult,
|
|
8
|
+
activateOkfKnowledgePlan,
|
|
9
|
+
createOkfKnowledgePlanFromDistillationBatch,
|
|
10
|
+
} from "../knowledge/index.ts";
|
|
11
|
+
import { type EvoDevExecutionEventV1, resolveProjectLogKey } from "../runtime-logs/index.ts";
|
|
6
12
|
|
|
7
13
|
export type EvolutionEvidenceSourceKind =
|
|
14
|
+
| "evodev-execution-event"
|
|
8
15
|
| "team-agent-trace"
|
|
9
16
|
| "session-trace"
|
|
10
17
|
| "task-contract"
|
|
11
18
|
| "verification"
|
|
12
19
|
| "review-summary"
|
|
13
|
-
| "user-feedback"
|
|
20
|
+
| "user-feedback"
|
|
21
|
+
| "code-agent-trace-ref";
|
|
14
22
|
|
|
15
23
|
export type EvolutionEvidenceEventKind =
|
|
16
24
|
| "trace"
|
|
@@ -77,10 +85,13 @@ export type EvolutionKnowledgeKind =
|
|
|
77
85
|
| "verification-pattern";
|
|
78
86
|
|
|
79
87
|
export type EvolutionReviewState =
|
|
88
|
+
| "auto-accepted"
|
|
80
89
|
| "auto-stored/unreviewed"
|
|
90
|
+
| "needs-human"
|
|
81
91
|
| "accepted"
|
|
82
92
|
| "rejected"
|
|
83
93
|
| "deferred"
|
|
94
|
+
| "stale"
|
|
84
95
|
| "deprecated"
|
|
85
96
|
| "superseded"
|
|
86
97
|
| "revoked";
|
|
@@ -129,6 +140,7 @@ export interface EvolutionEvidenceSourceRef {
|
|
|
129
140
|
path: string;
|
|
130
141
|
roleId: string | null;
|
|
131
142
|
rawContentStored: false;
|
|
143
|
+
externalContentCopied: false;
|
|
132
144
|
}
|
|
133
145
|
|
|
134
146
|
export interface EvolutionEvidenceEvent {
|
|
@@ -320,6 +332,34 @@ export interface EvolutionRepoProposal {
|
|
|
320
332
|
privacy: EvolutionPrivacyFields;
|
|
321
333
|
}
|
|
322
334
|
|
|
335
|
+
export interface EvolutionReviewCandidate {
|
|
336
|
+
schemaVersion: 1;
|
|
337
|
+
kind: "evolution-review-candidate";
|
|
338
|
+
id: string;
|
|
339
|
+
projectKey: string;
|
|
340
|
+
runId: string;
|
|
341
|
+
createdAt: string;
|
|
342
|
+
candidateKind: string;
|
|
343
|
+
title: string;
|
|
344
|
+
targetStore: string;
|
|
345
|
+
targetPath: string;
|
|
346
|
+
stableKey: string;
|
|
347
|
+
reviewState: "needs-human";
|
|
348
|
+
reasons: string[];
|
|
349
|
+
candidate: unknown;
|
|
350
|
+
provenance: {
|
|
351
|
+
runId: string;
|
|
352
|
+
evidenceWindowId: string;
|
|
353
|
+
evidenceRefs: string[];
|
|
354
|
+
createdBy: "evodev";
|
|
355
|
+
rawLogsStored: false;
|
|
356
|
+
rawPromptsStored: false;
|
|
357
|
+
sourceDumpsStored: false;
|
|
358
|
+
rawCommandOutputStored: false;
|
|
359
|
+
};
|
|
360
|
+
privacy: EvolutionPrivacyFields;
|
|
361
|
+
}
|
|
362
|
+
|
|
323
363
|
export interface EvolutionDistillationBatch {
|
|
324
364
|
schemaVersion: 1;
|
|
325
365
|
id: string;
|
|
@@ -356,14 +396,29 @@ export interface EvolutionWriteResult {
|
|
|
356
396
|
evosIndexPath: string;
|
|
357
397
|
}
|
|
358
398
|
|
|
399
|
+
export interface EvolutionActivationResult {
|
|
400
|
+
evidenceWindowPath: string;
|
|
401
|
+
batchPath: string;
|
|
402
|
+
okf: OkfKnowledgeActivationResult;
|
|
403
|
+
evosCasePaths: string[];
|
|
404
|
+
repoProposalPaths: string[];
|
|
405
|
+
evosIndexPath: string;
|
|
406
|
+
}
|
|
407
|
+
|
|
359
408
|
export interface EvolutionReviewSnapshot {
|
|
360
409
|
projectKey: string | null;
|
|
361
410
|
knowledgeRecords: EvolutionKnowledgeRecord[];
|
|
362
411
|
evosCases: EvolutionEvosCase[];
|
|
363
412
|
repoProposals: EvolutionRepoProposal[];
|
|
413
|
+
reviewCandidates: EvolutionReviewCandidate[];
|
|
364
414
|
triggers: EvolutionTriggerRecord[];
|
|
365
415
|
}
|
|
366
416
|
|
|
417
|
+
export interface EvolutionEvosCaseQueryResult {
|
|
418
|
+
cases: EvolutionEvosCase[];
|
|
419
|
+
warnings: string[];
|
|
420
|
+
}
|
|
421
|
+
|
|
367
422
|
export interface EvolutionResolvedPaths {
|
|
368
423
|
projectKey: string;
|
|
369
424
|
runId: string;
|
|
@@ -372,6 +427,7 @@ export interface EvolutionResolvedPaths {
|
|
|
372
427
|
evidenceWindowPath: string;
|
|
373
428
|
batchPath: string;
|
|
374
429
|
triggersDir: string;
|
|
430
|
+
reviewCandidatesDir: string;
|
|
375
431
|
repoProposalsDir: string;
|
|
376
432
|
repoProposalsIndexPath: string;
|
|
377
433
|
knowledgeProjectDir: string;
|
|
@@ -426,10 +482,13 @@ const MAX_EVIDENCE_EVENTS = 200;
|
|
|
426
482
|
const MAX_TEXT_LENGTH = 600;
|
|
427
483
|
const PROCESS_LOCK_STALE_MS = 5 * 60 * 1000;
|
|
428
484
|
const REVIEW_STATES: readonly EvolutionReviewState[] = [
|
|
485
|
+
"auto-accepted",
|
|
429
486
|
"auto-stored/unreviewed",
|
|
487
|
+
"needs-human",
|
|
430
488
|
"accepted",
|
|
431
489
|
"rejected",
|
|
432
490
|
"deferred",
|
|
491
|
+
"stale",
|
|
433
492
|
"deprecated",
|
|
434
493
|
"superseded",
|
|
435
494
|
"revoked",
|
|
@@ -548,9 +607,9 @@ const FORBIDDEN_RAW_KEYS = new Set([
|
|
|
548
607
|
"transcripttext",
|
|
549
608
|
]);
|
|
550
609
|
const SENSITIVE_TEXT_PATTERN =
|
|
551
|
-
/https?:\/\/\S+|\b(secret|token|password|passwd|api[_-]?key|apikey|credential|credentials|raw
|
|
610
|
+
/https?:\/\/\S+|\b(secret|token|password|passwd|api[_-]?key|apikey|credential|credentials|secret[\s_-]*token(?:[\s_-]*repro)?|raw[\s_-]*(?:log|logs|output|source|prompt)(?:[\s_-]*repro)?|shell[\s_-]*history|command[\s_-]*history)\b/i;
|
|
552
611
|
const SENSITIVE_TEXT_REPLACE_PATTERN =
|
|
553
|
-
/https?:\/\/\S+|\b(secret|token|password|passwd|api[_-]?key|apikey|credential|credentials|raw
|
|
612
|
+
/https?:\/\/\S+|\b(secret|token|password|passwd|api[_-]?key|apikey|credential|credentials|secret[\s_-]*token(?:[\s_-]*repro)?|raw[\s_-]*(?:log|logs|output|source|prompt)(?:[\s_-]*repro)?|shell[\s_-]*history|command[\s_-]*history)\b/gi;
|
|
554
613
|
|
|
555
614
|
export async function analyzeEvolutionRun(
|
|
556
615
|
input: EvolutionAnalyzeInput,
|
|
@@ -560,23 +619,54 @@ export async function analyzeEvolutionRun(
|
|
|
560
619
|
const paths = resolveEvolutionPaths({ homeDir: input.homeDir, projectKey, runId });
|
|
561
620
|
const warnings: string[] = [];
|
|
562
621
|
const sourceRefs: EvolutionEvidenceSourceRef[] = [];
|
|
622
|
+
const linkedTraceRefIds = new Set<string>();
|
|
563
623
|
const events: EvolutionEvidenceEvent[] = [];
|
|
564
|
-
const
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
624
|
+
const logsDir = resolveEvoDevPaths(input.homeDir).logsDir;
|
|
625
|
+
const projectRunAgentsDir = join(logsDir, "teams", projectKey, runId, "agents");
|
|
626
|
+
const legacyProjectRunAgentsDir = join(logsDir, projectKey, runId, "agents");
|
|
627
|
+
const agentDirs = (await pathExists(projectRunAgentsDir))
|
|
628
|
+
? await readdir(projectRunAgentsDir, { withFileTypes: true })
|
|
629
|
+
: [];
|
|
630
|
+
const legacyAgentDirs = (await pathExists(legacyProjectRunAgentsDir))
|
|
631
|
+
? await readdir(legacyProjectRunAgentsDir, { withFileTypes: true })
|
|
632
|
+
: [];
|
|
633
|
+
|
|
634
|
+
if (agentDirs.length === 0 && legacyAgentDirs.length === 0) {
|
|
572
635
|
warnings.push(
|
|
573
|
-
`No agent
|
|
636
|
+
`No agent execution event directory found: ${displayPath(input.homeDir, projectRunAgentsDir)}`,
|
|
574
637
|
);
|
|
575
638
|
} else {
|
|
576
|
-
const
|
|
577
|
-
for (const roleDir of
|
|
639
|
+
const rolesWithExecutionEvents = new Set<string>();
|
|
640
|
+
for (const roleDir of agentDirs.filter((entry) => entry.isDirectory())) {
|
|
578
641
|
const roleId = sanitizeId(roleDir.name);
|
|
579
|
-
const
|
|
642
|
+
const eventsPath = join(projectRunAgentsDir, roleDir.name, "events.jsonl");
|
|
643
|
+
if (!(await pathExists(eventsPath))) continue;
|
|
644
|
+
rolesWithExecutionEvents.add(roleId);
|
|
645
|
+
|
|
646
|
+
const sourceRef: EvolutionEvidenceSourceRef = {
|
|
647
|
+
id: `source-${sourceRefs.length + 1}`,
|
|
648
|
+
kind: "evodev-execution-event",
|
|
649
|
+
path: displayPath(input.homeDir, eventsPath),
|
|
650
|
+
roleId,
|
|
651
|
+
rawContentStored: false,
|
|
652
|
+
externalContentCopied: false,
|
|
653
|
+
};
|
|
654
|
+
sourceRefs.push(sourceRef);
|
|
655
|
+
const parsed = await readExecutionEvidenceEvents({
|
|
656
|
+
path: eventsPath,
|
|
657
|
+
roleId,
|
|
658
|
+
sourceRefId: sourceRef.id,
|
|
659
|
+
existingCount: events.length,
|
|
660
|
+
});
|
|
661
|
+
events.push(...parsed.events);
|
|
662
|
+
for (const traceRefId of parsed.traceRefIds) linkedTraceRefIds.add(traceRefId);
|
|
663
|
+
warnings.push(...parsed.warnings);
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
for (const roleDir of legacyAgentDirs.filter((entry) => entry.isDirectory())) {
|
|
667
|
+
const roleId = sanitizeId(roleDir.name);
|
|
668
|
+
if (rolesWithExecutionEvents.has(roleId)) continue;
|
|
669
|
+
const tracePath = join(legacyProjectRunAgentsDir, roleDir.name, "trace.log");
|
|
580
670
|
if (!(await pathExists(tracePath))) continue;
|
|
581
671
|
|
|
582
672
|
const sourceRef: EvolutionEvidenceSourceRef = {
|
|
@@ -585,6 +675,7 @@ export async function analyzeEvolutionRun(
|
|
|
585
675
|
path: displayPath(input.homeDir, tracePath),
|
|
586
676
|
roleId,
|
|
587
677
|
rawContentStored: false,
|
|
678
|
+
externalContentCopied: false,
|
|
588
679
|
};
|
|
589
680
|
sourceRefs.push(sourceRef);
|
|
590
681
|
const parsed = await readTraceEvidenceEvents({
|
|
@@ -598,6 +689,24 @@ export async function analyzeEvolutionRun(
|
|
|
598
689
|
}
|
|
599
690
|
}
|
|
600
691
|
|
|
692
|
+
for (const traceRefId of [...linkedTraceRefIds].sort()) {
|
|
693
|
+
try {
|
|
694
|
+
const record = await readCodeAgentTraceRef({ homeDir: input.homeDir, id: traceRefId });
|
|
695
|
+
sourceRefs.push({
|
|
696
|
+
id: `source-${sourceRefs.length + 1}`,
|
|
697
|
+
kind: "code-agent-trace-ref",
|
|
698
|
+
path: displayPath(input.homeDir, record.path),
|
|
699
|
+
roleId: record.ref.roleId,
|
|
700
|
+
rawContentStored: false,
|
|
701
|
+
externalContentCopied: false,
|
|
702
|
+
});
|
|
703
|
+
} catch {
|
|
704
|
+
warnings.push(
|
|
705
|
+
`Code Agent trace ref metadata not found for linked ref: ${sanitizeText(traceRefId)}.`,
|
|
706
|
+
);
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
|
|
601
710
|
if (events.length > MAX_EVIDENCE_EVENTS) {
|
|
602
711
|
warnings.push(
|
|
603
712
|
`Evidence events truncated from ${events.length} to ${MAX_EVIDENCE_EVENTS} metadata events.`,
|
|
@@ -675,7 +784,7 @@ export function createEvolutionDistillationBatch(input: {
|
|
|
675
784
|
body: [
|
|
676
785
|
`Run ${input.evidenceWindow.runId} produced ${input.evidenceWindow.events.length} redacted metadata evidence event(s).`,
|
|
677
786
|
`Observed role(s): ${roleIds.length === 0 ? "none" : roleIds.join(", ")}.`,
|
|
678
|
-
"This record is
|
|
787
|
+
"This record is derived from metadata-only evidence; active use depends on OKF plan review state.",
|
|
679
788
|
].join("\n"),
|
|
680
789
|
roleTags: roleIds,
|
|
681
790
|
tags: ["project-run", "auto-stored", "unreviewed"],
|
|
@@ -718,7 +827,7 @@ export function createEvolutionDistillationBatch(input: {
|
|
|
718
827
|
summary: createTriggerSummary(input.evidenceWindow),
|
|
719
828
|
},
|
|
720
829
|
intervention: {
|
|
721
|
-
summary: `Collected ${input.evidenceWindow.events.length}
|
|
830
|
+
summary: `Collected ${input.evidenceWindow.events.length} metadata event(s) and ${input.evidenceWindow.episodes.length} episode(s) from EvoDev execution logs.`,
|
|
722
831
|
roleIds,
|
|
723
832
|
},
|
|
724
833
|
result: {
|
|
@@ -891,6 +1000,106 @@ export async function writeEvolutionDistillationBatch(input: {
|
|
|
891
1000
|
};
|
|
892
1001
|
}
|
|
893
1002
|
|
|
1003
|
+
export async function activateEvolutionDistillationBatch(input: {
|
|
1004
|
+
homeDir: string;
|
|
1005
|
+
batch: EvolutionDistillationBatch;
|
|
1006
|
+
overwrite?: boolean;
|
|
1007
|
+
}): Promise<EvolutionActivationResult> {
|
|
1008
|
+
validateEvolutionDistillationBatch(input.batch);
|
|
1009
|
+
const paths = resolveEvolutionPaths({
|
|
1010
|
+
homeDir: input.homeDir,
|
|
1011
|
+
projectKey: input.batch.projectKey,
|
|
1012
|
+
runId: input.batch.runId,
|
|
1013
|
+
});
|
|
1014
|
+
const overwrite = input.overwrite === true;
|
|
1015
|
+
await writeJson(paths.evidenceWindowPath, input.batch.evidenceWindow, { overwrite });
|
|
1016
|
+
await writeJson(paths.batchPath, input.batch, { overwrite });
|
|
1017
|
+
|
|
1018
|
+
const plan = createOkfKnowledgePlanFromDistillationBatch(input.batch, {
|
|
1019
|
+
homeDir: input.homeDir,
|
|
1020
|
+
});
|
|
1021
|
+
const okf = await activateOkfKnowledgePlan({
|
|
1022
|
+
homeDir: input.homeDir,
|
|
1023
|
+
plan,
|
|
1024
|
+
overwrite,
|
|
1025
|
+
evidenceWindowPath: paths.evidenceWindowPath,
|
|
1026
|
+
});
|
|
1027
|
+
|
|
1028
|
+
const evosCasePaths: string[] = [];
|
|
1029
|
+
for (const evosCase of input.batch.evosCases) {
|
|
1030
|
+
const matchingCandidate = plan.candidates.find((candidate) => candidate.id === evosCase.id);
|
|
1031
|
+
const persistedEvosCase =
|
|
1032
|
+
matchingCandidate?.decision === "auto-accept"
|
|
1033
|
+
? { ...evosCase, reviewState: "auto-accepted" as const }
|
|
1034
|
+
: matchingCandidate?.decision === "needs-human"
|
|
1035
|
+
? { ...evosCase, reviewState: "needs-human" as const }
|
|
1036
|
+
: evosCase;
|
|
1037
|
+
validateEvolutionEvosCase(persistedEvosCase);
|
|
1038
|
+
const path = join(paths.evosCasesProjectDir, `${evosCase.id}.json`);
|
|
1039
|
+
await writeJson(path, persistedEvosCase, { overwrite });
|
|
1040
|
+
evosCasePaths.push(path);
|
|
1041
|
+
}
|
|
1042
|
+
|
|
1043
|
+
const repoProposalPaths: string[] = [];
|
|
1044
|
+
for (const proposal of input.batch.repoProposals) {
|
|
1045
|
+
const path = join(paths.repoProposalsDir, `${proposal.id}.json`);
|
|
1046
|
+
await writeJson(path, proposal, { overwrite });
|
|
1047
|
+
repoProposalPaths.push(path);
|
|
1048
|
+
}
|
|
1049
|
+
|
|
1050
|
+
const evosCasesRootDir = resolveEvoDevPaths(input.homeDir).evosCasesDir;
|
|
1051
|
+
const evosProjectKeys = await listDirectoryNames(evosCasesRootDir);
|
|
1052
|
+
const evosProjects = await Promise.all(
|
|
1053
|
+
evosProjectKeys.map(async (projectKey) => {
|
|
1054
|
+
const cases = await readJsonFiles(join(evosCasesRootDir, projectKey), parseEvosCase);
|
|
1055
|
+
return {
|
|
1056
|
+
projectKey,
|
|
1057
|
+
cases: cases.map((evosCase) => ({
|
|
1058
|
+
id: evosCase.id,
|
|
1059
|
+
title: evosCase.title,
|
|
1060
|
+
reviewState: evosCase.reviewState,
|
|
1061
|
+
})),
|
|
1062
|
+
};
|
|
1063
|
+
}),
|
|
1064
|
+
);
|
|
1065
|
+
await writeJson(
|
|
1066
|
+
paths.evosIndexPath,
|
|
1067
|
+
{
|
|
1068
|
+
schemaVersion: 1,
|
|
1069
|
+
updatedAt: input.batch.createdAt,
|
|
1070
|
+
projects: evosProjects,
|
|
1071
|
+
},
|
|
1072
|
+
{ overwrite: true },
|
|
1073
|
+
);
|
|
1074
|
+
|
|
1075
|
+
const allRunProposals = await readJsonFiles(paths.repoProposalsDir, parseRepoProposal);
|
|
1076
|
+
await writeJson(
|
|
1077
|
+
paths.repoProposalsIndexPath,
|
|
1078
|
+
{
|
|
1079
|
+
schemaVersion: 1,
|
|
1080
|
+
projectKey: input.batch.projectKey,
|
|
1081
|
+
runId: input.batch.runId,
|
|
1082
|
+
updatedAt: input.batch.createdAt,
|
|
1083
|
+
proposals: allRunProposals.map((proposal) => ({
|
|
1084
|
+
id: proposal.id,
|
|
1085
|
+
kind: proposal.kind,
|
|
1086
|
+
title: proposal.title,
|
|
1087
|
+
reviewState: proposal.reviewState,
|
|
1088
|
+
})),
|
|
1089
|
+
},
|
|
1090
|
+
{ overwrite: true },
|
|
1091
|
+
);
|
|
1092
|
+
|
|
1093
|
+
return {
|
|
1094
|
+
evidenceWindowPath: paths.evidenceWindowPath,
|
|
1095
|
+
batchPath: paths.batchPath,
|
|
1096
|
+
okf,
|
|
1097
|
+
evosCasePaths,
|
|
1098
|
+
repoProposalPaths,
|
|
1099
|
+
evosIndexPath: paths.evosIndexPath,
|
|
1100
|
+
};
|
|
1101
|
+
}
|
|
1102
|
+
|
|
894
1103
|
export async function readEvolutionReviewSnapshot(input: {
|
|
895
1104
|
homeDir: string;
|
|
896
1105
|
projectKey?: string;
|
|
@@ -898,11 +1107,12 @@ export async function readEvolutionReviewSnapshot(input: {
|
|
|
898
1107
|
const paths = resolveEvoDevPaths(input.homeDir);
|
|
899
1108
|
const projectKeys =
|
|
900
1109
|
input.projectKey === undefined
|
|
901
|
-
? await
|
|
1110
|
+
? await listEvolutionReviewProjectKeys(paths)
|
|
902
1111
|
: [sanitizeStorageId("projectKey", input.projectKey)];
|
|
903
1112
|
const knowledgeRecords: EvolutionKnowledgeRecord[] = [];
|
|
904
1113
|
const evosCases: EvolutionEvosCase[] = [];
|
|
905
1114
|
const repoProposals: EvolutionRepoProposal[] = [];
|
|
1115
|
+
const reviewCandidates: EvolutionReviewCandidate[] = [];
|
|
906
1116
|
const triggers: EvolutionTriggerRecord[] = [];
|
|
907
1117
|
|
|
908
1118
|
for (const projectKey of projectKeys) {
|
|
@@ -927,7 +1137,9 @@ export async function readEvolutionReviewSnapshot(input: {
|
|
|
927
1137
|
const runIds = await listDirectoryNames(projectStateDir);
|
|
928
1138
|
for (const runId of runIds) {
|
|
929
1139
|
const proposalsDir = join(projectStateDir, runId, "proposals");
|
|
1140
|
+
const reviewCandidatesDir = join(projectStateDir, runId, "review-candidates");
|
|
930
1141
|
repoProposals.push(...(await readJsonFiles(proposalsDir, parseRepoProposal)));
|
|
1142
|
+
reviewCandidates.push(...(await readJsonFiles(reviewCandidatesDir, parseReviewCandidate)));
|
|
931
1143
|
triggers.push(
|
|
932
1144
|
...(await readJsonFiles(join(projectStateDir, runId, "triggers"), parseTrigger)),
|
|
933
1145
|
);
|
|
@@ -940,10 +1152,29 @@ export async function readEvolutionReviewSnapshot(input: {
|
|
|
940
1152
|
knowledgeRecords,
|
|
941
1153
|
evosCases,
|
|
942
1154
|
repoProposals,
|
|
1155
|
+
reviewCandidates,
|
|
943
1156
|
triggers,
|
|
944
1157
|
};
|
|
945
1158
|
}
|
|
946
1159
|
|
|
1160
|
+
async function listEvolutionReviewProjectKeys(
|
|
1161
|
+
paths: ReturnType<typeof resolveEvoDevPaths>,
|
|
1162
|
+
): Promise<string[]> {
|
|
1163
|
+
const legacyKnowledgeProjectKeys = (
|
|
1164
|
+
await Promise.all(
|
|
1165
|
+
(
|
|
1166
|
+
await listDirectoryNames(paths.knowledgeDir)
|
|
1167
|
+
).map(async (projectKey) =>
|
|
1168
|
+
(await pathExists(join(paths.knowledgeDir, projectKey, "records"))) ? projectKey : null,
|
|
1169
|
+
),
|
|
1170
|
+
)
|
|
1171
|
+
).filter((projectKey): projectKey is string => projectKey !== null);
|
|
1172
|
+
return uniqueSorted([
|
|
1173
|
+
...legacyKnowledgeProjectKeys,
|
|
1174
|
+
...(await listDirectoryNames(paths.evosCasesDir)),
|
|
1175
|
+
]);
|
|
1176
|
+
}
|
|
1177
|
+
|
|
947
1178
|
export async function listEvolutionKnowledgeRecords(input: {
|
|
948
1179
|
homeDir: string;
|
|
949
1180
|
projectKey?: string;
|
|
@@ -956,6 +1187,59 @@ export async function listEvolutionKnowledgeRecords(input: {
|
|
|
956
1187
|
).knowledgeRecords.sort((left, right) => left.id.localeCompare(right.id));
|
|
957
1188
|
}
|
|
958
1189
|
|
|
1190
|
+
export async function listEvolutionEvosCases(input: {
|
|
1191
|
+
homeDir: string;
|
|
1192
|
+
projectKey?: string;
|
|
1193
|
+
roleId?: string;
|
|
1194
|
+
reviewStates?: EvolutionReviewState[];
|
|
1195
|
+
}): Promise<EvolutionEvosCaseQueryResult> {
|
|
1196
|
+
const paths = resolveEvoDevPaths(input.homeDir);
|
|
1197
|
+
const projectKeys =
|
|
1198
|
+
input.projectKey === undefined
|
|
1199
|
+
? await listDirectoryNames(paths.evosCasesDir)
|
|
1200
|
+
: [sanitizeStorageId("projectKey", input.projectKey)];
|
|
1201
|
+
const roleId = input.roleId === undefined ? null : sanitizeId(input.roleId);
|
|
1202
|
+
const reviewStates = input.reviewStates ?? ["accepted", "auto-accepted"];
|
|
1203
|
+
const cases: EvolutionEvosCase[] = [];
|
|
1204
|
+
const warnings: string[] = [];
|
|
1205
|
+
|
|
1206
|
+
for (const projectKey of projectKeys) {
|
|
1207
|
+
const projectDir = join(paths.evosCasesDir, projectKey);
|
|
1208
|
+
if (!(await pathExists(projectDir))) continue;
|
|
1209
|
+
const entries = await readdir(projectDir, { withFileTypes: true });
|
|
1210
|
+
for (const entry of entries.sort((left, right) => left.name.localeCompare(right.name))) {
|
|
1211
|
+
if (!entry.isFile() || !entry.name.endsWith(".json")) continue;
|
|
1212
|
+
const sourceLink = `cases/${projectKey}/${entry.name}`;
|
|
1213
|
+
try {
|
|
1214
|
+
const value = JSON.parse(await readFile(join(projectDir, entry.name), "utf8")) as unknown;
|
|
1215
|
+
const evosCase = parseEvosCase(value);
|
|
1216
|
+
if (!reviewStates.includes(evosCase.reviewState)) continue;
|
|
1217
|
+
if (
|
|
1218
|
+
roleId !== null &&
|
|
1219
|
+
evosCase.roleTags.length > 0 &&
|
|
1220
|
+
!evosCase.roleTags.includes(roleId)
|
|
1221
|
+
) {
|
|
1222
|
+
continue;
|
|
1223
|
+
}
|
|
1224
|
+
cases.push(evosCase);
|
|
1225
|
+
} catch {
|
|
1226
|
+
warnings.push(
|
|
1227
|
+
`Omitted unsafe or invalid evos case: ${sourceLink}. Run evodev knowledge lint.`,
|
|
1228
|
+
);
|
|
1229
|
+
}
|
|
1230
|
+
}
|
|
1231
|
+
}
|
|
1232
|
+
|
|
1233
|
+
return {
|
|
1234
|
+
cases: cases.sort((left, right) => {
|
|
1235
|
+
const project = left.projectKey.localeCompare(right.projectKey);
|
|
1236
|
+
if (project !== 0) return project;
|
|
1237
|
+
return left.id.localeCompare(right.id);
|
|
1238
|
+
}),
|
|
1239
|
+
warnings,
|
|
1240
|
+
};
|
|
1241
|
+
}
|
|
1242
|
+
|
|
959
1243
|
export async function readEvolutionKnowledgeRecordById(input: {
|
|
960
1244
|
homeDir: string;
|
|
961
1245
|
knowledgeId: string;
|
|
@@ -1057,8 +1341,8 @@ export function formatEvolutionDistillationBatch(batch: EvolutionDistillationBat
|
|
|
1057
1341
|
`Knowledge records: ${batch.knowledgeRecords.length}`,
|
|
1058
1342
|
`Evos cases: ${batch.evosCases.length}`,
|
|
1059
1343
|
`Repo proposals: ${batch.repoProposals.length}`,
|
|
1060
|
-
"Default review state: auto-
|
|
1061
|
-
"Runtime authority:
|
|
1344
|
+
"Default batch review state: contextual until the OKF plan auto-accepts or queues review.",
|
|
1345
|
+
"Runtime authority: accepted/auto-accepted only; hardBlocking=false.",
|
|
1062
1346
|
...(batch.knowledgeRecords.length === 0
|
|
1063
1347
|
? ["Knowledge: none"]
|
|
1064
1348
|
: [
|
|
@@ -1109,6 +1393,30 @@ export function formatEvolutionWriteResult(result: EvolutionWriteResult): string
|
|
|
1109
1393
|
].join("\n");
|
|
1110
1394
|
}
|
|
1111
1395
|
|
|
1396
|
+
export function formatEvolutionActivationResult(result: EvolutionActivationResult): string {
|
|
1397
|
+
return [
|
|
1398
|
+
"EvoDev evolution activate",
|
|
1399
|
+
"",
|
|
1400
|
+
`Evidence window: ${result.evidenceWindowPath}`,
|
|
1401
|
+
`Distillation batch: ${result.batchPath}`,
|
|
1402
|
+
`OKF concepts: ${result.okf.conceptPaths.length}`,
|
|
1403
|
+
...result.okf.conceptPaths.map((path) => ` - ${path}`),
|
|
1404
|
+
`OKF overlays: ${result.okf.overlayPaths.length}`,
|
|
1405
|
+
...result.okf.overlayPaths.map((path) => ` - ${path}`),
|
|
1406
|
+
`Skipped candidates: ${result.okf.skippedCandidates.length}`,
|
|
1407
|
+
...result.okf.skippedCandidates.map((id) => ` - ${id}`),
|
|
1408
|
+
`Needs human: ${result.okf.needsHumanCandidates.length}`,
|
|
1409
|
+
...result.okf.needsHumanCandidates.map((id) => ` - ${id}`),
|
|
1410
|
+
`Evos cases: ${result.evosCasePaths.length}`,
|
|
1411
|
+
...result.evosCasePaths.map((path) => ` - ${path}`),
|
|
1412
|
+
`Repo proposals: ${result.repoProposalPaths.length}`,
|
|
1413
|
+
...result.repoProposalPaths.map((path) => ` - ${path}`),
|
|
1414
|
+
`OKF derived indexes: ${result.okf.derivedIndexPaths.length}`,
|
|
1415
|
+
...result.okf.derivedIndexPaths.map((path) => ` - ${path}`),
|
|
1416
|
+
`Evos index: ${result.evosIndexPath}`,
|
|
1417
|
+
].join("\n");
|
|
1418
|
+
}
|
|
1419
|
+
|
|
1112
1420
|
export function formatEvolutionProcessResult(result: EvolutionProcessResult): string {
|
|
1113
1421
|
return [
|
|
1114
1422
|
"EvoDev evolution process",
|
|
@@ -1147,6 +1455,11 @@ export function formatEvolutionReviewSnapshot(snapshot: EvolutionReviewSnapshot)
|
|
|
1147
1455
|
(proposal) =>
|
|
1148
1456
|
` - ${proposal.id} (${proposal.projectKey}, ${proposal.kind}, ${proposal.reviewState}, ${proposal.confidence}): ${proposal.title}`,
|
|
1149
1457
|
),
|
|
1458
|
+
`Review candidates: ${snapshot.reviewCandidates.length}`,
|
|
1459
|
+
...snapshot.reviewCandidates.map(
|
|
1460
|
+
(candidate) =>
|
|
1461
|
+
` - ${candidate.id} (${candidate.projectKey}, ${candidate.candidateKind}, ${candidate.reviewState}): ${candidate.title}`,
|
|
1462
|
+
),
|
|
1150
1463
|
`Triggers: ${snapshot.triggers.length}`,
|
|
1151
1464
|
...snapshot.triggers.map(
|
|
1152
1465
|
(trigger) =>
|
|
@@ -1166,9 +1479,11 @@ export function resolveEvolutionPaths(input: {
|
|
|
1166
1479
|
const evolutionStateDir = join(paths.stateDir, "evolution");
|
|
1167
1480
|
const runStateDir = join(evolutionStateDir, projectKey, runId);
|
|
1168
1481
|
const repoProposalsDir = join(runStateDir, "proposals");
|
|
1482
|
+
const reviewCandidatesDir = join(runStateDir, "review-candidates");
|
|
1169
1483
|
const knowledgeProjectDir = join(paths.knowledgeDir, projectKey);
|
|
1170
1484
|
const evosCasesProjectDir = join(paths.evosCasesDir, projectKey);
|
|
1171
1485
|
assertPathDescendant(evolutionStateDir, runStateDir, "runStateDir");
|
|
1486
|
+
assertPathDescendant(evolutionStateDir, reviewCandidatesDir, "reviewCandidatesDir");
|
|
1172
1487
|
assertPathDescendant(paths.knowledgeDir, knowledgeProjectDir, "knowledgeProjectDir");
|
|
1173
1488
|
assertPathDescendant(paths.evosCasesDir, evosCasesProjectDir, "evosCasesProjectDir");
|
|
1174
1489
|
return {
|
|
@@ -1179,6 +1494,7 @@ export function resolveEvolutionPaths(input: {
|
|
|
1179
1494
|
evidenceWindowPath: join(runStateDir, "evidence-window.json"),
|
|
1180
1495
|
batchPath: join(runStateDir, "distillation-batch.json"),
|
|
1181
1496
|
triggersDir: join(runStateDir, "triggers"),
|
|
1497
|
+
reviewCandidatesDir,
|
|
1182
1498
|
repoProposalsDir,
|
|
1183
1499
|
repoProposalsIndexPath: join(runStateDir, "proposals.index.json"),
|
|
1184
1500
|
knowledgeProjectDir,
|
|
@@ -1204,6 +1520,9 @@ export function validateEvolutionEvidenceWindow(window: EvolutionEvidenceWindow)
|
|
|
1204
1520
|
assertString("sourceRefs.path", sourceRef.path);
|
|
1205
1521
|
if (sourceRef.rawContentStored !== false)
|
|
1206
1522
|
throw new Error("sourceRefs.rawContentStored must be false.");
|
|
1523
|
+
if (sourceRef.externalContentCopied !== false) {
|
|
1524
|
+
throw new Error("sourceRefs.externalContentCopied must be false.");
|
|
1525
|
+
}
|
|
1207
1526
|
}
|
|
1208
1527
|
if (!Array.isArray(window.events)) throw new Error("events must be an array.");
|
|
1209
1528
|
for (const event of window.events) {
|
|
@@ -1246,7 +1565,15 @@ export function validateEvolutionKnowledgeRecord(record: EvolutionKnowledgeRecor
|
|
|
1246
1565
|
assertEnum("kind", record.kind, KNOWLEDGE_KINDS);
|
|
1247
1566
|
assertEnum("reviewState", record.reviewState, REVIEW_STATES);
|
|
1248
1567
|
assertEnum("authority", record.authority, ["contextual", "reviewed"]);
|
|
1249
|
-
if (record.reviewState === "
|
|
1568
|
+
if (record.reviewState === "accepted" && record.authority !== "reviewed") {
|
|
1569
|
+
throw new Error("Accepted knowledge must use reviewed authority.");
|
|
1570
|
+
}
|
|
1571
|
+
if (
|
|
1572
|
+
(record.reviewState === "auto-stored/unreviewed" ||
|
|
1573
|
+
record.reviewState === "auto-accepted" ||
|
|
1574
|
+
record.reviewState === "needs-human") &&
|
|
1575
|
+
record.authority !== "contextual"
|
|
1576
|
+
) {
|
|
1250
1577
|
throw new Error("Unreviewed knowledge must remain contextual.");
|
|
1251
1578
|
}
|
|
1252
1579
|
if (record.runtime.hardBlocking !== false) {
|
|
@@ -1466,7 +1793,7 @@ export async function processEvolutionTriggers(
|
|
|
1466
1793
|
warnings: analysis.warnings,
|
|
1467
1794
|
now,
|
|
1468
1795
|
});
|
|
1469
|
-
await
|
|
1796
|
+
await activateEvolutionDistillationBatch({
|
|
1470
1797
|
homeDir: input.homeDir,
|
|
1471
1798
|
batch,
|
|
1472
1799
|
overwrite: true,
|
|
@@ -1597,6 +1924,57 @@ function validateEvolutionRepoProposal(proposal: EvolutionRepoProposal): void {
|
|
|
1597
1924
|
assertNoForbiddenRawFields(proposal);
|
|
1598
1925
|
}
|
|
1599
1926
|
|
|
1927
|
+
function validateEvolutionReviewCandidate(candidate: EvolutionReviewCandidate): void {
|
|
1928
|
+
if (!isRecord(candidate)) throw new Error("Review candidate must be an object.");
|
|
1929
|
+
if (candidate.schemaVersion !== 1) {
|
|
1930
|
+
throw new Error("Review candidate schemaVersion must be 1.");
|
|
1931
|
+
}
|
|
1932
|
+
if (candidate.kind !== "evolution-review-candidate") {
|
|
1933
|
+
throw new Error("Review candidate kind is invalid.");
|
|
1934
|
+
}
|
|
1935
|
+
assertString("reviewCandidate.id", candidate.id);
|
|
1936
|
+
assertString("reviewCandidate.projectKey", candidate.projectKey);
|
|
1937
|
+
assertString("reviewCandidate.runId", candidate.runId);
|
|
1938
|
+
assertString("reviewCandidate.createdAt", candidate.createdAt);
|
|
1939
|
+
assertString("reviewCandidate.candidateKind", candidate.candidateKind);
|
|
1940
|
+
assertString("reviewCandidate.title", candidate.title);
|
|
1941
|
+
assertString("reviewCandidate.targetStore", candidate.targetStore);
|
|
1942
|
+
assertString("reviewCandidate.targetPath", candidate.targetPath);
|
|
1943
|
+
assertString("reviewCandidate.stableKey", candidate.stableKey);
|
|
1944
|
+
if (candidate.reviewState !== "needs-human") {
|
|
1945
|
+
throw new Error("Review candidate reviewState must be needs-human.");
|
|
1946
|
+
}
|
|
1947
|
+
if (!Array.isArray(candidate.reasons))
|
|
1948
|
+
throw new Error("Review candidate reasons must be an array.");
|
|
1949
|
+
for (const reason of candidate.reasons) assertString("reviewCandidate.reasons", reason);
|
|
1950
|
+
if (!isRecord(candidate.provenance))
|
|
1951
|
+
throw new Error("Review candidate provenance must be an object.");
|
|
1952
|
+
assertString("reviewCandidate.provenance.runId", candidate.provenance.runId);
|
|
1953
|
+
assertString(
|
|
1954
|
+
"reviewCandidate.provenance.evidenceWindowId",
|
|
1955
|
+
candidate.provenance.evidenceWindowId,
|
|
1956
|
+
);
|
|
1957
|
+
if (candidate.provenance.createdBy !== "evodev") {
|
|
1958
|
+
throw new Error("Review candidate provenance.createdBy must be evodev.");
|
|
1959
|
+
}
|
|
1960
|
+
if (!Array.isArray(candidate.provenance.evidenceRefs)) {
|
|
1961
|
+
throw new Error("Review candidate evidenceRefs must be an array.");
|
|
1962
|
+
}
|
|
1963
|
+
for (const evidenceRef of candidate.provenance.evidenceRefs) {
|
|
1964
|
+
assertString("reviewCandidate.provenance.evidenceRefs", evidenceRef);
|
|
1965
|
+
}
|
|
1966
|
+
if (
|
|
1967
|
+
candidate.provenance.rawLogsStored !== false ||
|
|
1968
|
+
candidate.provenance.rawPromptsStored !== false ||
|
|
1969
|
+
candidate.provenance.sourceDumpsStored !== false ||
|
|
1970
|
+
candidate.provenance.rawCommandOutputStored !== false
|
|
1971
|
+
) {
|
|
1972
|
+
throw new Error("Review candidate provenance must remain metadata-only.");
|
|
1973
|
+
}
|
|
1974
|
+
assertPrivacy(candidate.privacy);
|
|
1975
|
+
assertNoForbiddenRawFields(candidate);
|
|
1976
|
+
}
|
|
1977
|
+
|
|
1600
1978
|
function validateEvolutionTriggerRecord(trigger: EvolutionTriggerRecord): void {
|
|
1601
1979
|
if (!isRecord(trigger)) throw new Error("Evolution trigger must be an object.");
|
|
1602
1980
|
if (trigger.schemaVersion !== 1) throw new Error("Evolution trigger schemaVersion must be 1.");
|
|
@@ -1620,6 +1998,90 @@ function validateEvolutionTriggerRecord(trigger: EvolutionTriggerRecord): void {
|
|
|
1620
1998
|
assertNoForbiddenRawFields(trigger);
|
|
1621
1999
|
}
|
|
1622
2000
|
|
|
2001
|
+
async function readExecutionEvidenceEvents(input: {
|
|
2002
|
+
path: string;
|
|
2003
|
+
roleId: string;
|
|
2004
|
+
sourceRefId: string;
|
|
2005
|
+
existingCount: number;
|
|
2006
|
+
}): Promise<{ events: EvolutionEvidenceEvent[]; warnings: string[]; traceRefIds: string[] }> {
|
|
2007
|
+
const raw = await readFile(input.path, "utf8");
|
|
2008
|
+
const lines = raw.split("\n");
|
|
2009
|
+
const events: EvolutionEvidenceEvent[] = [];
|
|
2010
|
+
const warnings: string[] = [];
|
|
2011
|
+
const traceRefIds = new Set<string>();
|
|
2012
|
+
|
|
2013
|
+
for (let index = 0; index < lines.length; index += 1) {
|
|
2014
|
+
const line = lines[index]?.trim();
|
|
2015
|
+
if (line === undefined || line === "") continue;
|
|
2016
|
+
|
|
2017
|
+
try {
|
|
2018
|
+
const value = JSON.parse(line) as unknown;
|
|
2019
|
+
if (!isRecord(value) || value.kind !== "evodev-execution-event") continue;
|
|
2020
|
+
const codeAgentTraceRefId = optionalString(value.codeAgentTraceRefId);
|
|
2021
|
+
if (codeAgentTraceRefId !== null) traceRefIds.add(codeAgentTraceRefId);
|
|
2022
|
+
const event = createEvidenceEventFromExecutionEvent({
|
|
2023
|
+
executionEvent: value as unknown as EvoDevExecutionEventV1,
|
|
2024
|
+
roleId: input.roleId,
|
|
2025
|
+
sourceRefId: input.sourceRefId,
|
|
2026
|
+
lineNumber: index + 1,
|
|
2027
|
+
sequence: input.existingCount + events.length + 1,
|
|
2028
|
+
});
|
|
2029
|
+
if (event !== null) events.push(event);
|
|
2030
|
+
} catch {
|
|
2031
|
+
warnings.push(
|
|
2032
|
+
`Skipped malformed execution event JSON at ${basename(input.path)}:${index + 1}.`,
|
|
2033
|
+
);
|
|
2034
|
+
}
|
|
2035
|
+
}
|
|
2036
|
+
|
|
2037
|
+
return { events, warnings, traceRefIds: [...traceRefIds] };
|
|
2038
|
+
}
|
|
2039
|
+
|
|
2040
|
+
function createEvidenceEventFromExecutionEvent(input: {
|
|
2041
|
+
executionEvent: EvoDevExecutionEventV1;
|
|
2042
|
+
roleId: string;
|
|
2043
|
+
sourceRefId: string;
|
|
2044
|
+
lineNumber: number;
|
|
2045
|
+
sequence: number;
|
|
2046
|
+
}): EvolutionEvidenceEvent | null {
|
|
2047
|
+
const metadata = isRecord(input.executionEvent.metadata) ? input.executionEvent.metadata : {};
|
|
2048
|
+
const phase = optionalString(metadata.phase);
|
|
2049
|
+
const target = optionalString(input.executionEvent.target);
|
|
2050
|
+
const eventType = optionalString(input.executionEvent.eventType);
|
|
2051
|
+
const summarySource =
|
|
2052
|
+
optionalString(input.executionEvent.summary) ?? [target, phase].filter(Boolean).join(" ");
|
|
2053
|
+
if (summarySource.trim() === "") return null;
|
|
2054
|
+
const normalizedType = normalizeEvolutionEventType(eventType);
|
|
2055
|
+
const kind = classifyEvidenceEvent({
|
|
2056
|
+
error: null,
|
|
2057
|
+
summary: summarySource,
|
|
2058
|
+
target,
|
|
2059
|
+
eventType,
|
|
2060
|
+
phase,
|
|
2061
|
+
});
|
|
2062
|
+
const trigger = decideEvolutionTrigger({
|
|
2063
|
+
eventType: normalizedType,
|
|
2064
|
+
summary: summarySource,
|
|
2065
|
+
kind,
|
|
2066
|
+
});
|
|
2067
|
+
|
|
2068
|
+
return {
|
|
2069
|
+
id: `event-${String(input.sequence).padStart(4, "0")}`,
|
|
2070
|
+
kind,
|
|
2071
|
+
eventType: normalizedType,
|
|
2072
|
+
hookEventId: sanitizeOptionalId(input.executionEvent.eventId),
|
|
2073
|
+
occurredAt: optionalString(input.executionEvent.timestamp),
|
|
2074
|
+
summary: sanitizeText(summarySource),
|
|
2075
|
+
roleId: sanitizeOptionalId(input.executionEvent.roleId) ?? sanitizeId(input.roleId),
|
|
2076
|
+
taskId: sanitizeOptionalId(input.executionEvent.taskId),
|
|
2077
|
+
evidenceRef: `${input.sourceRefId}#L${input.lineNumber}`,
|
|
2078
|
+
episodeIds: [],
|
|
2079
|
+
triggerStrength: trigger.strength,
|
|
2080
|
+
triggerReason: trigger.reason,
|
|
2081
|
+
rawContentStored: false,
|
|
2082
|
+
};
|
|
2083
|
+
}
|
|
2084
|
+
|
|
1623
2085
|
async function readTraceEvidenceEvents(input: {
|
|
1624
2086
|
path: string;
|
|
1625
2087
|
roleId: string;
|
|
@@ -2034,6 +2496,11 @@ function parseRepoProposal(value: unknown): EvolutionRepoProposal {
|
|
|
2034
2496
|
return value as EvolutionRepoProposal;
|
|
2035
2497
|
}
|
|
2036
2498
|
|
|
2499
|
+
function parseReviewCandidate(value: unknown): EvolutionReviewCandidate {
|
|
2500
|
+
validateEvolutionReviewCandidate(value as EvolutionReviewCandidate);
|
|
2501
|
+
return value as EvolutionReviewCandidate;
|
|
2502
|
+
}
|
|
2503
|
+
|
|
2037
2504
|
function parseTrigger(value: unknown): EvolutionTriggerRecord {
|
|
2038
2505
|
validateEvolutionTriggerRecord(value as EvolutionTriggerRecord);
|
|
2039
2506
|
return value as EvolutionTriggerRecord;
|
|
@@ -2146,6 +2613,10 @@ async function listDirectoryNames(dir: string): Promise<string[]> {
|
|
|
2146
2613
|
.sort();
|
|
2147
2614
|
}
|
|
2148
2615
|
|
|
2616
|
+
function uniqueSorted(values: string[]): string[] {
|
|
2617
|
+
return [...new Set(values)].sort();
|
|
2618
|
+
}
|
|
2619
|
+
|
|
2149
2620
|
async function writeJson(
|
|
2150
2621
|
path: string,
|
|
2151
2622
|
value: unknown,
|