@evo-dev/core 0.0.1-alpha.5 → 0.0.1-alpha.6
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 +0 -36
- package/dist/index.js +710 -1590
- package/package.json +1 -1
- package/src/agents/index.ts +0 -264
- package/src/code-agent-traces/index.ts +8 -2
- package/src/daemon/index.ts +0 -40
- package/src/evolution/candidates/index.ts +77 -18
- package/src/evolution/evidence/session-memory/storage.ts +50 -0
- package/src/evolution/evidence/session-memory/updater.ts +8 -1
- package/src/evolution/review/index.ts +3 -9
- package/src/evolution/schema.ts +0 -1
- package/src/hooks/index.ts +91 -179
- package/src/index.ts +1 -2
- package/src/projects/index.ts +453 -0
- package/src/workflow/index.ts +3 -21
- package/src/project/index.ts +0 -507
- package/src/task/index.ts +0 -840
package/dist/index.js
CHANGED
|
@@ -120,6 +120,7 @@ __export(exports_candidates, {
|
|
|
120
120
|
readEvolutionReviewSnapshot: () => readEvolutionReviewSnapshot,
|
|
121
121
|
readEvolutionRepoProposalById: () => readEvolutionRepoProposalById,
|
|
122
122
|
readEvolutionKnowledgeRecordById: () => readEvolutionKnowledgeRecordById,
|
|
123
|
+
markEvolutionRepoProposalApplied: () => markEvolutionRepoProposalApplied,
|
|
123
124
|
listEvolutionKnowledgeReviewHistory: () => listEvolutionKnowledgeReviewHistory,
|
|
124
125
|
listEvolutionKnowledgeRecords: () => listEvolutionKnowledgeRecords,
|
|
125
126
|
listEvolutionEvosCases: () => listEvolutionEvosCases
|
|
@@ -1167,23 +1168,64 @@ async function updateEvolutionRepoProposalReviewState(input) {
|
|
|
1167
1168
|
}
|
|
1168
1169
|
};
|
|
1169
1170
|
validateEvolutionRepoProposal(next);
|
|
1170
|
-
await
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1171
|
+
await writeRepoProposalAndIndex({ homeDir: input.homeDir, proposal: next, changedAt });
|
|
1172
|
+
return { path, record: next, changed: true };
|
|
1173
|
+
});
|
|
1174
|
+
}
|
|
1175
|
+
async function markEvolutionRepoProposalApplied(input) {
|
|
1176
|
+
return await withEvolutionReviewDecisionLock({ homeDir: input.homeDir, kind: "repo-proposal", itemId: input.proposalId }, async () => {
|
|
1177
|
+
const record = await readEvolutionRepoProposalById(input);
|
|
1178
|
+
if (!hasConcreteRepoProposalChanges(record)) {
|
|
1179
|
+
throw new Error("Repo proposal has no concrete repository changes to apply.");
|
|
1180
|
+
}
|
|
1181
|
+
if (input.expectedReviewState !== undefined && record.reviewState !== input.expectedReviewState) {
|
|
1182
|
+
throw new Error("Repo proposal review state changed before it was marked applied.");
|
|
1183
|
+
}
|
|
1184
|
+
const paths = resolveEvolutionPaths({
|
|
1185
|
+
homeDir: input.homeDir,
|
|
1186
|
+
projectKey: record.projectKey,
|
|
1187
|
+
runId: record.provenance.runId
|
|
1188
|
+
});
|
|
1189
|
+
const path = join3(paths.repoProposalsDir, `${record.id}.json`);
|
|
1190
|
+
if (record.reviewState === "applied")
|
|
1191
|
+
return { path, record, changed: false };
|
|
1192
|
+
if (record.reviewState !== "accepted") {
|
|
1193
|
+
throw new Error("Only accepted repo proposals can be marked applied.");
|
|
1194
|
+
}
|
|
1195
|
+
const changedAt = normalizeTimestamp2(input.now);
|
|
1196
|
+
const next = {
|
|
1197
|
+
...record,
|
|
1198
|
+
reviewState: "applied",
|
|
1199
|
+
reviewStateChangedAt: changedAt
|
|
1200
|
+
};
|
|
1201
|
+
validateEvolutionRepoProposal(next);
|
|
1202
|
+
await writeRepoProposalAndIndex({ homeDir: input.homeDir, proposal: next, changedAt });
|
|
1184
1203
|
return { path, record: next, changed: true };
|
|
1185
1204
|
});
|
|
1186
1205
|
}
|
|
1206
|
+
async function writeRepoProposalAndIndex(input) {
|
|
1207
|
+
const paths = resolveEvolutionPaths({
|
|
1208
|
+
homeDir: input.homeDir,
|
|
1209
|
+
projectKey: input.proposal.projectKey,
|
|
1210
|
+
runId: input.proposal.provenance.runId
|
|
1211
|
+
});
|
|
1212
|
+
await writeJsonFile(join3(paths.repoProposalsDir, `${input.proposal.id}.json`), input.proposal, {
|
|
1213
|
+
overwrite: true
|
|
1214
|
+
});
|
|
1215
|
+
const allRunProposals = await readJsonFiles(paths.repoProposalsDir, parseRepoProposal);
|
|
1216
|
+
await writeJsonFile(paths.repoProposalsIndexPath, {
|
|
1217
|
+
schemaVersion: 1,
|
|
1218
|
+
projectKey: input.proposal.projectKey,
|
|
1219
|
+
runId: input.proposal.provenance.runId,
|
|
1220
|
+
updatedAt: input.changedAt,
|
|
1221
|
+
proposals: allRunProposals.map((proposal) => ({
|
|
1222
|
+
id: proposal.id,
|
|
1223
|
+
kind: proposal.kind,
|
|
1224
|
+
title: proposal.title,
|
|
1225
|
+
reviewState: proposal.reviewState
|
|
1226
|
+
}))
|
|
1227
|
+
}, { overwrite: true });
|
|
1228
|
+
}
|
|
1187
1229
|
function parseKnowledgeReviewHistoryRecord(value) {
|
|
1188
1230
|
if (!isRecord2(value))
|
|
1189
1231
|
throw new Error("Knowledge review history record must be an object.");
|
|
@@ -4567,16 +4609,6 @@ var DEFAULT_AGENT_PERMISSIONS = {
|
|
|
4567
4609
|
};
|
|
4568
4610
|
var FORBIDDEN_INPUTS = ["rawPrompts", "secrets", "rawCommandLogs", "sourceCorpus"];
|
|
4569
4611
|
var SAFE_AGENT_ID_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._-]{0,99}$/;
|
|
4570
|
-
var PRIVACY_ADVISORY_PATTERN = /raw prompt|rawprompt|source corpus|sourcecorpus|secret|\.env|raw command|raw log|internal url|private url|network|write files|run commands|spawn agents|write memory/i;
|
|
4571
|
-
var LENS_TO_EXPERTISE = {
|
|
4572
|
-
review: ["code-review"],
|
|
4573
|
-
qa: ["qa", "verification"],
|
|
4574
|
-
security: ["security", "privacy"],
|
|
4575
|
-
release: ["release", "packaging"],
|
|
4576
|
-
architecture: ["architecture"],
|
|
4577
|
-
migration: ["migration", "impact-analysis"],
|
|
4578
|
-
test: ["testing"]
|
|
4579
|
-
};
|
|
4580
4612
|
function createDefaultAgentPermissions() {
|
|
4581
4613
|
return { ...DEFAULT_AGENT_PERMISSIONS };
|
|
4582
4614
|
}
|
|
@@ -4623,50 +4655,6 @@ function parseAgentProfile(value) {
|
|
|
4623
4655
|
async function readAgentProfile(path) {
|
|
4624
4656
|
return parseAgentProfile(JSON.parse(await readFile4(path, "utf8")));
|
|
4625
4657
|
}
|
|
4626
|
-
function composeAgentDryRun(input) {
|
|
4627
|
-
const mode = input.contract.route.mode;
|
|
4628
|
-
const workflowId = input.workflowId ?? input.contract.route.workflowId;
|
|
4629
|
-
const warnings = [];
|
|
4630
|
-
const permissions = createDefaultAgentPermissions();
|
|
4631
|
-
const requestedLenses = dedupeLenses(input.lenses ?? []);
|
|
4632
|
-
const advisories = collectPrivacyBoundaryAdvisories(input.contract, requestedLenses, workflowId);
|
|
4633
|
-
if (mode === "minimal") {
|
|
4634
|
-
return {
|
|
4635
|
-
ok: advisories.length === 0,
|
|
4636
|
-
taskId: input.contract.taskId,
|
|
4637
|
-
mode,
|
|
4638
|
-
workflowId,
|
|
4639
|
-
agents: [],
|
|
4640
|
-
permissions,
|
|
4641
|
-
mergePlan: createMergePlan([]),
|
|
4642
|
-
warnings,
|
|
4643
|
-
advisories,
|
|
4644
|
-
rationale: "Minimal mode does not force dynamic agents by default."
|
|
4645
|
-
};
|
|
4646
|
-
}
|
|
4647
|
-
const selectedLenses = selectLenses(mode, requestedLenses, workflowId, input.contract.route.requiredReview);
|
|
4648
|
-
const agents = selectedLenses.map((lens) => ({
|
|
4649
|
-
profile: createDynamicProfile(lens, input.contract, permissions),
|
|
4650
|
-
lens,
|
|
4651
|
-
reason: createLensReason(lens, mode, workflowId),
|
|
4652
|
-
plannedOnly: true
|
|
4653
|
-
}));
|
|
4654
|
-
if (mode === "standard" && agents.length > 1) {
|
|
4655
|
-
warnings.push("Standard mode advisory selected more than one reviewer/triager.");
|
|
4656
|
-
}
|
|
4657
|
-
return {
|
|
4658
|
-
ok: advisories.length === 0,
|
|
4659
|
-
taskId: input.contract.taskId,
|
|
4660
|
-
mode,
|
|
4661
|
-
workflowId,
|
|
4662
|
-
agents,
|
|
4663
|
-
permissions,
|
|
4664
|
-
mergePlan: createMergePlan(agents),
|
|
4665
|
-
warnings,
|
|
4666
|
-
advisories,
|
|
4667
|
-
rationale: agents.length === 0 ? "No dynamic agents selected; deterministic verification may be sufficient." : `Selected ${agents.length} planned-only agent(s) based on mode/workflow/lenses.`
|
|
4668
|
-
};
|
|
4669
|
-
}
|
|
4670
4658
|
function loadAgentContextDryRun(profile) {
|
|
4671
4659
|
const parsed = parseAgentProfile(profile);
|
|
4672
4660
|
return {
|
|
@@ -4760,36 +4748,6 @@ function mergeReviewFindings(outputs) {
|
|
|
4760
4748
|
}
|
|
4761
4749
|
return { acceptedFindings: [...byKey.values()], unresolvedConflicts: conflicts };
|
|
4762
4750
|
}
|
|
4763
|
-
function formatAgentComposeDryRun(plan) {
|
|
4764
|
-
return [
|
|
4765
|
-
"EvoDev agent compose dry-run",
|
|
4766
|
-
"",
|
|
4767
|
-
`Task: ${plan.taskId}`,
|
|
4768
|
-
`Mode: ${plan.mode ?? "not routed"}`,
|
|
4769
|
-
`Workflow: ${plan.workflowId ?? "none"}`,
|
|
4770
|
-
`Rationale: ${plan.rationale}`,
|
|
4771
|
-
"No-write/no-spawn: true",
|
|
4772
|
-
"Privacy: local-private, metadata-only, raw prompts/source/secrets/logs forbidden",
|
|
4773
|
-
"Agents:",
|
|
4774
|
-
...plan.agents.length === 0 ? [" - none"] : plan.agents.map((agent) => ` - ${agent.profile.id} [${agent.lens}] traits=${agent.profile.traits.expertise.join(",")} schema=${agent.profile.output.schema} plannedOnly=${agent.plannedOnly}`),
|
|
4775
|
-
"Inputs:",
|
|
4776
|
-
...plan.agents.length === 0 ? [" - none"] : [
|
|
4777
|
-
` - required: ${plan.agents[0].profile.inputs.required.join(",")}`,
|
|
4778
|
-
` - optional: ${plan.agents[0].profile.inputs.optional.join(",")}`,
|
|
4779
|
-
` - forbidden: ${plan.agents[0].profile.inputs.forbidden.join(",")}`
|
|
4780
|
-
],
|
|
4781
|
-
"Permissions:",
|
|
4782
|
-
...Object.entries(plan.permissions).map(([key, value]) => ` - ${key}: ${value}`),
|
|
4783
|
-
"Merge plan:",
|
|
4784
|
-
` - strategy: ${plan.mergePlan.strategy}`,
|
|
4785
|
-
` - outputSchema: ${plan.mergePlan.outputSchema ?? "none"}`,
|
|
4786
|
-
"Warnings:",
|
|
4787
|
-
...plan.warnings.length === 0 ? [" - none"] : plan.warnings.map((warning) => ` - ${warning}`),
|
|
4788
|
-
"Advisories:",
|
|
4789
|
-
...plan.advisories.length === 0 ? [" - none"] : plan.advisories.map((advisory) => ` - ${advisory}`)
|
|
4790
|
-
].join(`
|
|
4791
|
-
`);
|
|
4792
|
-
}
|
|
4793
4751
|
function formatAgentContextDryRun(bundle) {
|
|
4794
4752
|
return [
|
|
4795
4753
|
"EvoDev agent load-context dry-run",
|
|
@@ -4807,98 +4765,6 @@ function formatAgentContextDryRun(bundle) {
|
|
|
4807
4765
|
].join(`
|
|
4808
4766
|
`);
|
|
4809
4767
|
}
|
|
4810
|
-
function createDynamicProfile(lens, contract, permissions) {
|
|
4811
|
-
return {
|
|
4812
|
-
version: 1,
|
|
4813
|
-
id: `dynamic-${lens}-${contract.taskId}`.slice(0, 100),
|
|
4814
|
-
name: `Dynamic ${lens} reviewer`,
|
|
4815
|
-
persistence: "dynamic",
|
|
4816
|
-
description: `Task-scoped ${lens} profile for metadata-only dry-run planning.`,
|
|
4817
|
-
traits: {
|
|
4818
|
-
expertise: LENS_TO_EXPERTISE[lens],
|
|
4819
|
-
stance: ["skeptical-reviewer"],
|
|
4820
|
-
approach: ["evidence-first", "advisory"],
|
|
4821
|
-
domain: ["software-rd"]
|
|
4822
|
-
},
|
|
4823
|
-
inputs: {
|
|
4824
|
-
required: ["taskContract", "scope", "evidenceSummary"],
|
|
4825
|
-
optional: ["workflowPlan"],
|
|
4826
|
-
forbidden: FORBIDDEN_INPUTS
|
|
4827
|
-
},
|
|
4828
|
-
permissions,
|
|
4829
|
-
output: {
|
|
4830
|
-
schema: "review-findings-v1",
|
|
4831
|
-
requiredFields: ["summary", "findings", "confidence", "evidenceRefs"]
|
|
4832
|
-
},
|
|
4833
|
-
privacy: {
|
|
4834
|
-
classification: "local-private",
|
|
4835
|
-
metadataOnly: true,
|
|
4836
|
-
rawPromptStored: false,
|
|
4837
|
-
sourceContentStored: false
|
|
4838
|
-
}
|
|
4839
|
-
};
|
|
4840
|
-
}
|
|
4841
|
-
function selectLenses(mode, requested, workflowId, requiredReview) {
|
|
4842
|
-
if (requested.length > 0)
|
|
4843
|
-
return mode === "standard" ? requested.slice(0, 1) : requested;
|
|
4844
|
-
if (mode === "standard")
|
|
4845
|
-
return requiredReview.length > 0 ? ["review"] : [];
|
|
4846
|
-
if (mode === "rigorous") {
|
|
4847
|
-
if (workflowId?.includes("security"))
|
|
4848
|
-
return ["security", "review"];
|
|
4849
|
-
if (workflowId?.includes("release"))
|
|
4850
|
-
return ["release", "security"];
|
|
4851
|
-
if (workflowId?.includes("migration"))
|
|
4852
|
-
return ["migration", "architecture"];
|
|
4853
|
-
if (workflowId?.includes("architecture"))
|
|
4854
|
-
return ["architecture", "review"];
|
|
4855
|
-
return requiredReview.length > 0 ? ["review", "qa"] : [];
|
|
4856
|
-
}
|
|
4857
|
-
return [];
|
|
4858
|
-
}
|
|
4859
|
-
function createMergePlan(agents) {
|
|
4860
|
-
if (agents.length === 0)
|
|
4861
|
-
return {
|
|
4862
|
-
strategy: "none",
|
|
4863
|
-
outputSchema: null,
|
|
4864
|
-
dedupeBy: [],
|
|
4865
|
-
conflictPolicy: [],
|
|
4866
|
-
advisoryCategories: []
|
|
4867
|
-
};
|
|
4868
|
-
if (agents.length === 1)
|
|
4869
|
-
return {
|
|
4870
|
-
strategy: "single-output",
|
|
4871
|
-
outputSchema: agents[0].profile.output.schema,
|
|
4872
|
-
dedupeBy: [],
|
|
4873
|
-
conflictPolicy: [],
|
|
4874
|
-
advisoryCategories: []
|
|
4875
|
-
};
|
|
4876
|
-
return {
|
|
4877
|
-
strategy: "dedupe-preserve-conflicts",
|
|
4878
|
-
outputSchema: "review-findings-v1",
|
|
4879
|
-
dedupeBy: ["category", "path", "line", "title"],
|
|
4880
|
-
conflictPolicy: ["preserve dissent", "keep higher severity unless evidence refutes"],
|
|
4881
|
-
advisoryCategories: ["security", "privacy", "release"]
|
|
4882
|
-
};
|
|
4883
|
-
}
|
|
4884
|
-
function createLensReason(lens, mode, workflowId) {
|
|
4885
|
-
return `${lens} lens selected for ${mode ?? "unrouted"} mode${workflowId ? ` and workflow ${workflowId}` : ""}.`;
|
|
4886
|
-
}
|
|
4887
|
-
function collectPrivacyBoundaryAdvisories(contract, lenses, workflowId) {
|
|
4888
|
-
const text2 = [
|
|
4889
|
-
contract.source.summary,
|
|
4890
|
-
contract.currentState.summary,
|
|
4891
|
-
contract.targetState.summary,
|
|
4892
|
-
contract.route.rationale,
|
|
4893
|
-
workflowId ?? "",
|
|
4894
|
-
...contract.scope.allowedOperations,
|
|
4895
|
-
...contract.scope.requiresUserConfirmation,
|
|
4896
|
-
...lenses
|
|
4897
|
-
].join(" ");
|
|
4898
|
-
return PRIVACY_ADVISORY_PATTERN.test(text2) ? [
|
|
4899
|
-
"Agent planning detected privacy/risky context requiring raw data, writes, commands, network, spawning, or memory."
|
|
4900
|
-
] : [];
|
|
4901
|
-
}
|
|
4902
4768
|
function validateRequiredObject(output, fields) {
|
|
4903
4769
|
if (!isRecord4(output))
|
|
4904
4770
|
return { ok: false, errors: ["Output must be an object."] };
|
|
@@ -4911,9 +4777,6 @@ function normalizeAgentPermissions(value) {
|
|
|
4911
4777
|
function isKnownOutputSchema(value) {
|
|
4912
4778
|
return value === "review-findings-v1" || value === "verification-summary-v1" || value === "design-options-v1";
|
|
4913
4779
|
}
|
|
4914
|
-
function dedupeLenses(lenses) {
|
|
4915
|
-
return [...new Set(lenses)];
|
|
4916
|
-
}
|
|
4917
4780
|
function severityRank(severity) {
|
|
4918
4781
|
return { info: 0, low: 1, medium: 2, high: 3, critical: 4 }[severity];
|
|
4919
4782
|
}
|
|
@@ -5141,8 +5004,12 @@ __export(exports_code_agent_traces, {
|
|
|
5141
5004
|
listCodeAgentTraceRefs: () => listCodeAgentTraceRefs,
|
|
5142
5005
|
createCodeAgentTraceRef: () => createCodeAgentTraceRef
|
|
5143
5006
|
});
|
|
5144
|
-
import { mkdir as mkdir5, readFile as
|
|
5145
|
-
import { dirname as
|
|
5007
|
+
import { mkdir as mkdir5, readFile as readFile7, readdir as readdir7, stat as stat5, writeFile as writeFile4 } from "node:fs/promises";
|
|
5008
|
+
import { dirname as dirname6, isAbsolute as isAbsolute5, join as join8, normalize, relative as relative5 } from "node:path";
|
|
5009
|
+
|
|
5010
|
+
// packages/core/src/projects/index.ts
|
|
5011
|
+
import { lstat, readFile as readFile6, readdir as readdir6, realpath, stat as stat4 } from "node:fs/promises";
|
|
5012
|
+
import { basename, dirname as dirname5, isAbsolute as isAbsolute4, join as join7, parse } from "node:path";
|
|
5146
5013
|
|
|
5147
5014
|
// packages/core/src/runtime-logs/index.ts
|
|
5148
5015
|
var exports_runtime_logs = {};
|
|
@@ -5613,6 +5480,299 @@ function isRecord5(value) {
|
|
|
5613
5480
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
5614
5481
|
}
|
|
5615
5482
|
|
|
5483
|
+
// packages/core/src/projects/index.ts
|
|
5484
|
+
class ProjectRegistrationError extends Error {
|
|
5485
|
+
code;
|
|
5486
|
+
constructor(code, message) {
|
|
5487
|
+
super(message);
|
|
5488
|
+
this.name = "ProjectRegistrationError";
|
|
5489
|
+
this.code = code;
|
|
5490
|
+
}
|
|
5491
|
+
}
|
|
5492
|
+
function resolveProjectRegistryPaths(homeDir) {
|
|
5493
|
+
const paths = resolveEvoDevPaths(homeDir);
|
|
5494
|
+
const discoveredDir = join7(paths.stateDir, "projects", "discovered");
|
|
5495
|
+
const registeredDir = join7(paths.rootDir, "projects");
|
|
5496
|
+
return {
|
|
5497
|
+
discoveredDir,
|
|
5498
|
+
registeredDir,
|
|
5499
|
+
discoveredPath: (projectKey) => join7(discoveredDir, `${projectKey}.json`),
|
|
5500
|
+
registeredPath: (projectKey) => join7(registeredDir, projectKey, "workspace.json")
|
|
5501
|
+
};
|
|
5502
|
+
}
|
|
5503
|
+
async function resolveProjectWorkspaceFromCwd(input) {
|
|
5504
|
+
if (!isAbsolute4(input.cwd))
|
|
5505
|
+
return null;
|
|
5506
|
+
let cwd;
|
|
5507
|
+
try {
|
|
5508
|
+
const info = await stat4(input.cwd);
|
|
5509
|
+
if (!info.isDirectory())
|
|
5510
|
+
return null;
|
|
5511
|
+
cwd = await realpath(input.cwd);
|
|
5512
|
+
} catch (error) {
|
|
5513
|
+
if (isNotFoundError(error))
|
|
5514
|
+
return null;
|
|
5515
|
+
throw error;
|
|
5516
|
+
}
|
|
5517
|
+
const gitRoot = await findNearestGitRoot(cwd);
|
|
5518
|
+
const workspaceRoot = gitRoot ?? cwd;
|
|
5519
|
+
return {
|
|
5520
|
+
projectKey: sanitizeStorageId(resolveProjectLogKey(input.homeDir, workspaceRoot), "project"),
|
|
5521
|
+
displayName: basename(workspaceRoot) || parse(workspaceRoot).root,
|
|
5522
|
+
workspaceRoot,
|
|
5523
|
+
workspaceKind: gitRoot === null ? "directory" : "git",
|
|
5524
|
+
cwd
|
|
5525
|
+
};
|
|
5526
|
+
}
|
|
5527
|
+
async function recordDiscoveredProject(input) {
|
|
5528
|
+
const workspace = await resolveProjectWorkspaceFromCwd(input);
|
|
5529
|
+
if (workspace === null)
|
|
5530
|
+
return null;
|
|
5531
|
+
const now = normalizeTimestamp(input.now);
|
|
5532
|
+
const paths = resolveProjectRegistryPaths(input.homeDir);
|
|
5533
|
+
const path = paths.discoveredPath(workspace.projectKey);
|
|
5534
|
+
const existing = await readDiscoveredProject(path);
|
|
5535
|
+
const record = {
|
|
5536
|
+
schemaVersion: 1,
|
|
5537
|
+
kind: "discovered-project",
|
|
5538
|
+
projectKey: workspace.projectKey,
|
|
5539
|
+
displayName: workspace.displayName,
|
|
5540
|
+
workspaceRoot: workspace.workspaceRoot,
|
|
5541
|
+
lastCwd: workspace.cwd,
|
|
5542
|
+
workspaceKind: workspace.workspaceKind,
|
|
5543
|
+
sourceTargets: mergeTargets(existing?.sourceTargets ?? [], [input.target]),
|
|
5544
|
+
lastSessionKey: normalizeSessionKey(input.sessionKey ?? existing?.lastSessionKey ?? null),
|
|
5545
|
+
firstSeenAt: existing?.firstSeenAt ?? now,
|
|
5546
|
+
lastSeenAt: now,
|
|
5547
|
+
localOnly: true,
|
|
5548
|
+
sourceContentStored: false
|
|
5549
|
+
};
|
|
5550
|
+
await writeJsonFile(path, record);
|
|
5551
|
+
return { record, path };
|
|
5552
|
+
}
|
|
5553
|
+
async function listDiscoveredProjects(input) {
|
|
5554
|
+
const paths = resolveProjectRegistryPaths(input.homeDir);
|
|
5555
|
+
const records = new Map;
|
|
5556
|
+
for (const path of await listJsonFilePaths(paths.discoveredDir)) {
|
|
5557
|
+
const record = await readDiscoveredProject(path);
|
|
5558
|
+
if (record !== null)
|
|
5559
|
+
mergeDiscoveredRecord(records, record);
|
|
5560
|
+
}
|
|
5561
|
+
for (const binding of await listLegacyHookBindings(input.homeDir)) {
|
|
5562
|
+
if (binding.cwd === null)
|
|
5563
|
+
continue;
|
|
5564
|
+
const workspace = await resolveProjectWorkspaceFromCwd({
|
|
5565
|
+
homeDir: input.homeDir,
|
|
5566
|
+
cwd: binding.cwd
|
|
5567
|
+
});
|
|
5568
|
+
if (workspace === null)
|
|
5569
|
+
continue;
|
|
5570
|
+
mergeDiscoveredRecord(records, {
|
|
5571
|
+
schemaVersion: 1,
|
|
5572
|
+
kind: "discovered-project",
|
|
5573
|
+
projectKey: workspace.projectKey,
|
|
5574
|
+
displayName: workspace.displayName,
|
|
5575
|
+
workspaceRoot: workspace.workspaceRoot,
|
|
5576
|
+
lastCwd: workspace.cwd,
|
|
5577
|
+
workspaceKind: workspace.workspaceKind,
|
|
5578
|
+
sourceTargets: [binding.target],
|
|
5579
|
+
lastSessionKey: normalizeSessionKey(binding.sessionKey),
|
|
5580
|
+
firstSeenAt: binding.updatedAt,
|
|
5581
|
+
lastSeenAt: binding.updatedAt,
|
|
5582
|
+
localOnly: true,
|
|
5583
|
+
sourceContentStored: false
|
|
5584
|
+
});
|
|
5585
|
+
}
|
|
5586
|
+
return [...records.values()].sort((left, right) => right.lastSeenAt.localeCompare(left.lastSeenAt) || left.projectKey.localeCompare(right.projectKey));
|
|
5587
|
+
}
|
|
5588
|
+
async function listRegisteredProjects(input) {
|
|
5589
|
+
const { registeredDir } = resolveProjectRegistryPaths(input.homeDir);
|
|
5590
|
+
const records = [];
|
|
5591
|
+
for (const projectKey of await listDirectoryNames2(registeredDir)) {
|
|
5592
|
+
const record = await readRegisteredProject(join7(registeredDir, projectKey, "workspace.json"));
|
|
5593
|
+
if (record !== null && record.projectKey === projectKey)
|
|
5594
|
+
records.push(record);
|
|
5595
|
+
}
|
|
5596
|
+
return records.sort((left, right) => right.lastSeenAt.localeCompare(left.lastSeenAt) || left.projectKey.localeCompare(right.projectKey));
|
|
5597
|
+
}
|
|
5598
|
+
async function registerDiscoveredProject(input) {
|
|
5599
|
+
assertProjectKey(input.projectKey);
|
|
5600
|
+
const paths = resolveProjectRegistryPaths(input.homeDir);
|
|
5601
|
+
const path = paths.registeredPath(input.projectKey);
|
|
5602
|
+
const existing = await readRegisteredProject(path);
|
|
5603
|
+
if (existing !== null)
|
|
5604
|
+
return { record: existing, path, changed: false };
|
|
5605
|
+
const discovered = (await listDiscoveredProjects({ homeDir: input.homeDir })).find((record2) => record2.projectKey === input.projectKey);
|
|
5606
|
+
if (discovered === undefined) {
|
|
5607
|
+
throw new ProjectRegistrationError("not-found", "The project must be discovered by a local Code Agent session before it can be added.");
|
|
5608
|
+
}
|
|
5609
|
+
const record = {
|
|
5610
|
+
schemaVersion: 1,
|
|
5611
|
+
kind: "registered-project",
|
|
5612
|
+
projectKey: discovered.projectKey,
|
|
5613
|
+
displayName: discovered.displayName,
|
|
5614
|
+
workspaceRoot: discovered.workspaceRoot,
|
|
5615
|
+
workspaceKind: discovered.workspaceKind,
|
|
5616
|
+
registeredAt: normalizeTimestamp(input.now),
|
|
5617
|
+
lastSeenAt: discovered.lastSeenAt,
|
|
5618
|
+
localOnly: true,
|
|
5619
|
+
sourceContentStored: false
|
|
5620
|
+
};
|
|
5621
|
+
try {
|
|
5622
|
+
await writeJsonFile(path, record, { overwrite: false });
|
|
5623
|
+
return { record, path, changed: true };
|
|
5624
|
+
} catch (error) {
|
|
5625
|
+
if (!isFileExistsError(error))
|
|
5626
|
+
throw error;
|
|
5627
|
+
const concurrent = await readRegisteredProject(path);
|
|
5628
|
+
if (concurrent !== null)
|
|
5629
|
+
return { record: concurrent, path, changed: false };
|
|
5630
|
+
throw new ProjectRegistrationError("conflict", "The project registration already exists.");
|
|
5631
|
+
}
|
|
5632
|
+
}
|
|
5633
|
+
async function findNearestGitRoot(cwd) {
|
|
5634
|
+
let current = cwd;
|
|
5635
|
+
for (;; ) {
|
|
5636
|
+
try {
|
|
5637
|
+
const marker = await lstat(join7(current, ".git"));
|
|
5638
|
+
if (marker.isDirectory() || marker.isFile())
|
|
5639
|
+
return current;
|
|
5640
|
+
} catch (error) {
|
|
5641
|
+
if (!isNotFoundError(error))
|
|
5642
|
+
throw error;
|
|
5643
|
+
}
|
|
5644
|
+
const parent = dirname5(current);
|
|
5645
|
+
if (parent === current)
|
|
5646
|
+
return null;
|
|
5647
|
+
current = parent;
|
|
5648
|
+
}
|
|
5649
|
+
}
|
|
5650
|
+
async function readDiscoveredProject(path) {
|
|
5651
|
+
try {
|
|
5652
|
+
return parseDiscoveredProject(JSON.parse(await readFile6(path, "utf8")));
|
|
5653
|
+
} catch (error) {
|
|
5654
|
+
if (isNotFoundError(error) || error instanceof ProjectRegistrationError)
|
|
5655
|
+
return null;
|
|
5656
|
+
throw error;
|
|
5657
|
+
}
|
|
5658
|
+
}
|
|
5659
|
+
async function readRegisteredProject(path) {
|
|
5660
|
+
try {
|
|
5661
|
+
return parseRegisteredProject(JSON.parse(await readFile6(path, "utf8")));
|
|
5662
|
+
} catch (error) {
|
|
5663
|
+
if (isNotFoundError(error) || error instanceof ProjectRegistrationError)
|
|
5664
|
+
return null;
|
|
5665
|
+
throw error;
|
|
5666
|
+
}
|
|
5667
|
+
}
|
|
5668
|
+
function parseDiscoveredProject(value) {
|
|
5669
|
+
const record = requireRecord(value);
|
|
5670
|
+
if (record.schemaVersion !== 1 || record.kind !== "discovered-project" || !isProjectKey(record.projectKey) || !isNonEmptyString2(record.displayName) || !isAbsoluteString(record.workspaceRoot) || !isAbsoluteString(record.lastCwd) || !isWorkspaceKind(record.workspaceKind) || !isDiscoveryTargetArray(record.sourceTargets) || !(record.lastSessionKey === null || isNonEmptyString2(record.lastSessionKey)) || !isNonEmptyString2(record.firstSeenAt) || !isNonEmptyString2(record.lastSeenAt) || record.localOnly !== true || record.sourceContentStored !== false) {
|
|
5671
|
+
throw new ProjectRegistrationError("invalid", "Discovered project record is invalid.");
|
|
5672
|
+
}
|
|
5673
|
+
return record;
|
|
5674
|
+
}
|
|
5675
|
+
function parseRegisteredProject(value) {
|
|
5676
|
+
const record = requireRecord(value);
|
|
5677
|
+
if (record.schemaVersion !== 1 || record.kind !== "registered-project" || !isProjectKey(record.projectKey) || !isNonEmptyString2(record.displayName) || !isAbsoluteString(record.workspaceRoot) || !isWorkspaceKind(record.workspaceKind) || !isNonEmptyString2(record.registeredAt) || !isNonEmptyString2(record.lastSeenAt) || record.localOnly !== true || record.sourceContentStored !== false) {
|
|
5678
|
+
throw new ProjectRegistrationError("invalid", "Registered project record is invalid.");
|
|
5679
|
+
}
|
|
5680
|
+
return record;
|
|
5681
|
+
}
|
|
5682
|
+
function mergeDiscoveredRecord(records, next) {
|
|
5683
|
+
const previous = records.get(next.projectKey);
|
|
5684
|
+
if (previous === undefined) {
|
|
5685
|
+
records.set(next.projectKey, next);
|
|
5686
|
+
return;
|
|
5687
|
+
}
|
|
5688
|
+
const newer = next.lastSeenAt >= previous.lastSeenAt ? next : previous;
|
|
5689
|
+
records.set(next.projectKey, {
|
|
5690
|
+
...newer,
|
|
5691
|
+
sourceTargets: mergeTargets(previous.sourceTargets, next.sourceTargets),
|
|
5692
|
+
firstSeenAt: previous.firstSeenAt <= next.firstSeenAt ? previous.firstSeenAt : next.firstSeenAt,
|
|
5693
|
+
lastSeenAt: previous.lastSeenAt >= next.lastSeenAt ? previous.lastSeenAt : next.lastSeenAt
|
|
5694
|
+
});
|
|
5695
|
+
}
|
|
5696
|
+
function mergeTargets(left, right) {
|
|
5697
|
+
return [...new Set([...left, ...right])].sort();
|
|
5698
|
+
}
|
|
5699
|
+
function normalizeSessionKey(value) {
|
|
5700
|
+
if (value === null || value === undefined)
|
|
5701
|
+
return null;
|
|
5702
|
+
return /^session-[a-f0-9]{16}$/u.test(value) ? value : `session-${sha256Short(value)}`;
|
|
5703
|
+
}
|
|
5704
|
+
async function listLegacyHookBindings(homeDir) {
|
|
5705
|
+
const roots = [
|
|
5706
|
+
join7(homeDir, ".evodev", "state", "hooks", "sessions"),
|
|
5707
|
+
join7(homeDir, ".evodev", "STATE", "hooks", "sessions")
|
|
5708
|
+
];
|
|
5709
|
+
const bindings = [];
|
|
5710
|
+
for (const root of roots) {
|
|
5711
|
+
for (const sessionDir of await listDirectoryNames2(root)) {
|
|
5712
|
+
try {
|
|
5713
|
+
const value = JSON.parse(await readFile6(join7(root, sessionDir, "binding.json"), "utf8"));
|
|
5714
|
+
if (value === null || value.target !== "claude" && value.target !== "codex" || !isNonEmptyString2(value.sessionKey) || !(value.cwd === null || isAbsoluteString(value.cwd)) || !isNonEmptyString2(value.updatedAt)) {
|
|
5715
|
+
continue;
|
|
5716
|
+
}
|
|
5717
|
+
bindings.push({
|
|
5718
|
+
target: value.target,
|
|
5719
|
+
sessionKey: value.sessionKey,
|
|
5720
|
+
cwd: value.cwd,
|
|
5721
|
+
updatedAt: value.updatedAt
|
|
5722
|
+
});
|
|
5723
|
+
} catch (error) {
|
|
5724
|
+
if (!isNotFoundError(error) && !(error instanceof SyntaxError))
|
|
5725
|
+
throw error;
|
|
5726
|
+
}
|
|
5727
|
+
}
|
|
5728
|
+
}
|
|
5729
|
+
return bindings;
|
|
5730
|
+
}
|
|
5731
|
+
async function listJsonFilePaths(path) {
|
|
5732
|
+
try {
|
|
5733
|
+
return (await readdir6(path, { withFileTypes: true })).filter((entry) => entry.isFile() && entry.name.endsWith(".json")).map((entry) => join7(path, entry.name)).sort();
|
|
5734
|
+
} catch (error) {
|
|
5735
|
+
if (isNotFoundError(error))
|
|
5736
|
+
return [];
|
|
5737
|
+
throw error;
|
|
5738
|
+
}
|
|
5739
|
+
}
|
|
5740
|
+
async function listDirectoryNames2(path) {
|
|
5741
|
+
try {
|
|
5742
|
+
return (await readdir6(path, { withFileTypes: true })).filter((entry) => entry.isDirectory()).map((entry) => entry.name).sort();
|
|
5743
|
+
} catch (error) {
|
|
5744
|
+
if (isNotFoundError(error))
|
|
5745
|
+
return [];
|
|
5746
|
+
throw error;
|
|
5747
|
+
}
|
|
5748
|
+
}
|
|
5749
|
+
function assertProjectKey(value) {
|
|
5750
|
+
if (!isProjectKey(value)) {
|
|
5751
|
+
throw new ProjectRegistrationError("invalid", "projectKey is invalid.");
|
|
5752
|
+
}
|
|
5753
|
+
}
|
|
5754
|
+
function isProjectKey(value) {
|
|
5755
|
+
return typeof value === "string" && value !== "." && value !== ".." && /^[A-Za-z0-9._-]{1,120}$/.test(value);
|
|
5756
|
+
}
|
|
5757
|
+
function isNonEmptyString2(value) {
|
|
5758
|
+
return typeof value === "string" && value.trim() !== "";
|
|
5759
|
+
}
|
|
5760
|
+
function isAbsoluteString(value) {
|
|
5761
|
+
return isNonEmptyString2(value) && isAbsolute4(value);
|
|
5762
|
+
}
|
|
5763
|
+
function isWorkspaceKind(value) {
|
|
5764
|
+
return value === "git" || value === "directory";
|
|
5765
|
+
}
|
|
5766
|
+
function isDiscoveryTargetArray(value) {
|
|
5767
|
+
return Array.isArray(value) && value.length > 0 && value.every((item) => item === "claude" || item === "codex");
|
|
5768
|
+
}
|
|
5769
|
+
function requireRecord(value) {
|
|
5770
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
5771
|
+
throw new ProjectRegistrationError("invalid", "Project record must be an object.");
|
|
5772
|
+
}
|
|
5773
|
+
return value;
|
|
5774
|
+
}
|
|
5775
|
+
|
|
5616
5776
|
// packages/core/src/code-agent-traces/index.ts
|
|
5617
5777
|
var CODE_AGENT_TRACE_TARGETS = ["claude", "codex"];
|
|
5618
5778
|
var CODE_AGENT_TRACE_REF_SOURCES = [
|
|
@@ -5624,11 +5784,11 @@ var CODE_AGENT_TRACE_REF_SOURCES = [
|
|
|
5624
5784
|
var SENSITIVE_IDENTIFIER_PATTERN = /https?:\/\/\S+|(^|[^a-z0-9])(secret|token|password|passwd|private|internal|api[_-]?key|apikey|credential|credentials|stdout|stderr|transcript|formattedresponse|additionalcontext)([^a-z0-9]|$)|raw[\s_-]?(payload|prompt|output|source)|source[\s_-]?dump/i;
|
|
5625
5785
|
var SENSITIVE_PATH_PATTERN = /(^|[/\\._-])(secret|token|password|passwd|api[_-]?key|apikey|credential|credentials)([/\\._-]|$)/i;
|
|
5626
5786
|
function resolveCodeAgentTraceRefPaths(homeDir) {
|
|
5627
|
-
const rootDir =
|
|
5787
|
+
const rootDir = join8(resolveEvoDevPaths(homeDir).stateDir, "code-agent-traces");
|
|
5628
5788
|
return {
|
|
5629
5789
|
rootDir,
|
|
5630
|
-
claudeDir:
|
|
5631
|
-
codexDir:
|
|
5790
|
+
claudeDir: join8(rootDir, "claude"),
|
|
5791
|
+
codexDir: join8(rootDir, "codex")
|
|
5632
5792
|
};
|
|
5633
5793
|
}
|
|
5634
5794
|
function createCodeAgentTraceRef(input) {
|
|
@@ -5701,7 +5861,7 @@ async function writeCodeAgentTraceRef(input) {
|
|
|
5701
5861
|
target: ref.target,
|
|
5702
5862
|
sessionKey: ref.sessionKey
|
|
5703
5863
|
});
|
|
5704
|
-
await mkdir5(
|
|
5864
|
+
await mkdir5(dirname6(path), { recursive: true });
|
|
5705
5865
|
await writeFile4(path, `${JSON.stringify(ref, null, 2)}
|
|
5706
5866
|
`, "utf8");
|
|
5707
5867
|
return { ref, path };
|
|
@@ -5713,7 +5873,7 @@ async function readCodeAgentTraceRef(input) {
|
|
|
5713
5873
|
target: targetAndSession.target,
|
|
5714
5874
|
sessionKey: targetAndSession.sessionKey
|
|
5715
5875
|
});
|
|
5716
|
-
const ref = sanitizeCodeAgentTraceRefForWrite(parseCodeAgentTraceRef(JSON.parse(await
|
|
5876
|
+
const ref = sanitizeCodeAgentTraceRefForWrite(parseCodeAgentTraceRef(JSON.parse(await readFile7(path, "utf8"))));
|
|
5717
5877
|
if (ref.id !== input.id)
|
|
5718
5878
|
throw new Error(`Code Agent trace ref id mismatch: ${input.id}`);
|
|
5719
5879
|
return { ref, path };
|
|
@@ -5727,8 +5887,8 @@ async function listCodeAgentTraceRefs(input) {
|
|
|
5727
5887
|
for (const entry of entries) {
|
|
5728
5888
|
if (!entry.isFile() || !entry.name.endsWith(".json"))
|
|
5729
5889
|
continue;
|
|
5730
|
-
const path =
|
|
5731
|
-
const ref = sanitizeCodeAgentTraceRefForWrite(parseCodeAgentTraceRef(JSON.parse(await
|
|
5890
|
+
const path = join8(dir, entry.name);
|
|
5891
|
+
const ref = sanitizeCodeAgentTraceRefForWrite(parseCodeAgentTraceRef(JSON.parse(await readFile7(path, "utf8"))));
|
|
5732
5892
|
if (input.projectKey !== undefined && ref.projectKey !== input.projectKey)
|
|
5733
5893
|
continue;
|
|
5734
5894
|
if (input.runId !== undefined && ref.runId !== input.runId)
|
|
@@ -5763,15 +5923,17 @@ async function recordCodeAgentTraceRefFromHook(input) {
|
|
|
5763
5923
|
payload: input.payload,
|
|
5764
5924
|
environment
|
|
5765
5925
|
});
|
|
5926
|
+
const cwd = optionalString4(input.payload.cwd);
|
|
5927
|
+
const workspace = team === null && cwd !== null ? await resolveProjectWorkspaceFromCwd({ homeDir: input.homeDir, cwd }) : null;
|
|
5766
5928
|
const source = sessionIdFromPayload !== null || tracePathFromPayload !== null ? "hook-payload" : "environment";
|
|
5767
5929
|
const ref = createCodeAgentTraceRef({
|
|
5768
5930
|
target: input.target,
|
|
5769
5931
|
sessionKey: resolveTraceSessionKey(input.payload),
|
|
5770
5932
|
nativeSessionId,
|
|
5771
|
-
projectKey: team?.projectKey ?? null,
|
|
5933
|
+
projectKey: team?.projectKey ?? workspace?.projectKey ?? null,
|
|
5772
5934
|
runId: team?.runId ?? null,
|
|
5773
5935
|
roleId: team?.roleId ?? null,
|
|
5774
|
-
cwd
|
|
5936
|
+
cwd,
|
|
5775
5937
|
discoveredAt: input.now,
|
|
5776
5938
|
source,
|
|
5777
5939
|
tracePath: normalizedPath,
|
|
@@ -5799,7 +5961,7 @@ function sanitizeCodeAgentTraceRefForWrite(ref) {
|
|
|
5799
5961
|
});
|
|
5800
5962
|
}
|
|
5801
5963
|
function resolveCodeAgentTraceRefPath(input) {
|
|
5802
|
-
return
|
|
5964
|
+
return join8(resolveCodeAgentTraceRefTargetDir(input.homeDir, input.target), `${sanitizePersistentIdentifier2(input.sessionKey, "session")}.json`);
|
|
5803
5965
|
}
|
|
5804
5966
|
function resolveCodeAgentTraceRefTargetDir(homeDir, target) {
|
|
5805
5967
|
const paths = resolveCodeAgentTraceRefPaths(homeDir);
|
|
@@ -5816,7 +5978,7 @@ function parseTraceRefId(id) {
|
|
|
5816
5978
|
}
|
|
5817
5979
|
async function readDirectoryEntries(dir) {
|
|
5818
5980
|
try {
|
|
5819
|
-
return await
|
|
5981
|
+
return await readdir7(dir, { withFileTypes: true });
|
|
5820
5982
|
} catch (error) {
|
|
5821
5983
|
if (isNotFoundError2(error))
|
|
5822
5984
|
return [];
|
|
@@ -5842,7 +6004,7 @@ function normalizeTracePathForInspection(value, notes) {
|
|
|
5842
6004
|
notes.push("trace path omitted: URLs are not stored");
|
|
5843
6005
|
return null;
|
|
5844
6006
|
}
|
|
5845
|
-
if (!
|
|
6007
|
+
if (!isAbsolute5(trimmed)) {
|
|
5846
6008
|
notes.push("trace path omitted: relative paths are not trusted");
|
|
5847
6009
|
return null;
|
|
5848
6010
|
}
|
|
@@ -5868,7 +6030,7 @@ async function inspectTracePath(input) {
|
|
|
5868
6030
|
const notes = [];
|
|
5869
6031
|
let exists = null;
|
|
5870
6032
|
try {
|
|
5871
|
-
await
|
|
6033
|
+
await stat5(input.tracePath);
|
|
5872
6034
|
exists = true;
|
|
5873
6035
|
} catch (error) {
|
|
5874
6036
|
if (isNotFoundError2(error)) {
|
|
@@ -5886,11 +6048,11 @@ async function inspectTracePath(input) {
|
|
|
5886
6048
|
return { exists, trusted, notes };
|
|
5887
6049
|
}
|
|
5888
6050
|
function resolveTargetUserDir(input) {
|
|
5889
|
-
return normalize(
|
|
6051
|
+
return normalize(join8(stripTrailingSlash3(input.homeDir), input.target === "claude" ? ".claude" : ".codex"));
|
|
5890
6052
|
}
|
|
5891
6053
|
function isDescendant(parent, child) {
|
|
5892
6054
|
const relation = relative5(normalize(parent), normalize(child));
|
|
5893
|
-
return relation === "" || !relation.startsWith("..") && !
|
|
6055
|
+
return relation === "" || !relation.startsWith("..") && !isAbsolute5(relation);
|
|
5894
6056
|
}
|
|
5895
6057
|
function hasRawTraversalSegment(path) {
|
|
5896
6058
|
return path.split(/[/\\]+/).some((part) => part === "..");
|
|
@@ -6039,6 +6201,7 @@ __export(exports_session_memory, {
|
|
|
6039
6201
|
readSessionCursor: () => readSessionCursor,
|
|
6040
6202
|
readLineRange: () => readLineRange,
|
|
6041
6203
|
parseSessionMemoryPolicy: () => parseSessionMemoryPolicy,
|
|
6204
|
+
listSessionMemoryStates: () => listSessionMemoryStates,
|
|
6042
6205
|
listSessionEvidenceSegments: () => listSessionEvidenceSegments,
|
|
6043
6206
|
createDefaultSessionMemoryPolicy: () => createDefaultSessionMemoryPolicy,
|
|
6044
6207
|
appendRawEvent: () => appendRawEvent,
|
|
@@ -6046,24 +6209,24 @@ __export(exports_session_memory, {
|
|
|
6046
6209
|
});
|
|
6047
6210
|
|
|
6048
6211
|
// packages/core/src/evolution/evidence/session-memory/paths.ts
|
|
6049
|
-
import { join as
|
|
6212
|
+
import { join as join9 } from "node:path";
|
|
6050
6213
|
function resolveSessionMemoryPaths(input) {
|
|
6051
|
-
const rootDir =
|
|
6214
|
+
const rootDir = join9(resolveEvoDevPaths(input.homeDir).stateDir, "session-memory");
|
|
6052
6215
|
const projectKey = sanitizeStorageId(input.projectKey, "project");
|
|
6053
6216
|
const sessionKey = sanitizeStorageId(input.sessionKey, "session");
|
|
6054
|
-
const projectDir =
|
|
6055
|
-
const sessionDir =
|
|
6056
|
-
const segmentsDir =
|
|
6217
|
+
const projectDir = join9(rootDir, projectKey);
|
|
6218
|
+
const sessionDir = join9(projectDir, sessionKey);
|
|
6219
|
+
const segmentsDir = join9(sessionDir, "segments");
|
|
6057
6220
|
return {
|
|
6058
6221
|
rootDir,
|
|
6059
6222
|
projectDir,
|
|
6060
6223
|
sessionDir,
|
|
6061
|
-
statePath:
|
|
6062
|
-
cursorPath:
|
|
6063
|
-
eventsPath:
|
|
6224
|
+
statePath: join9(sessionDir, "state.json"),
|
|
6225
|
+
cursorPath: join9(sessionDir, "cursor.json"),
|
|
6226
|
+
eventsPath: join9(sessionDir, "events.jsonl"),
|
|
6064
6227
|
segmentsDir,
|
|
6065
|
-
segmentPath: (segmentId) =>
|
|
6066
|
-
indexPath:
|
|
6228
|
+
segmentPath: (segmentId) => join9(segmentsDir, `${sanitizeStorageId(segmentId, "segment")}.json`),
|
|
6229
|
+
indexPath: join9(sessionDir, "index.json")
|
|
6067
6230
|
};
|
|
6068
6231
|
}
|
|
6069
6232
|
// packages/core/src/evolution/evidence/session-memory/policy.ts
|
|
@@ -6093,8 +6256,8 @@ function parseSessionMemoryPolicy(value) {
|
|
|
6093
6256
|
};
|
|
6094
6257
|
}
|
|
6095
6258
|
// packages/core/src/evolution/evidence/session-memory/storage.ts
|
|
6096
|
-
import { appendFile as appendFile2, mkdir as mkdir6, readFile as
|
|
6097
|
-
import { dirname as
|
|
6259
|
+
import { appendFile as appendFile2, mkdir as mkdir6, readFile as readFile8, readdir as readdir8, writeFile as writeFile5 } from "node:fs/promises";
|
|
6260
|
+
import { dirname as dirname7, join as join10 } from "node:path";
|
|
6098
6261
|
|
|
6099
6262
|
// packages/core/src/evolution/evidence/session-memory/constants.ts
|
|
6100
6263
|
var DEFAULT_MAX_RAW_EVENT_BYTES = 64 * 1024;
|
|
@@ -6113,8 +6276,8 @@ class SessionMemoryEvidenceError extends Error {
|
|
|
6113
6276
|
}
|
|
6114
6277
|
}
|
|
6115
6278
|
async function listSessionEvidenceSegments(input) {
|
|
6116
|
-
const rootDir =
|
|
6117
|
-
const projectDirs = input.projectKey === undefined ? await
|
|
6279
|
+
const rootDir = join10(resolveEvoDevPaths(input.homeDir).stateDir, "session-memory");
|
|
6280
|
+
const projectDirs = input.projectKey === undefined ? await listDirectoryNames3(rootDir) : [input.projectKey];
|
|
6118
6281
|
const segments = [];
|
|
6119
6282
|
for (const projectKey of projectDirs) {
|
|
6120
6283
|
const paths = resolveSessionMemoryPaths({
|
|
@@ -6122,7 +6285,7 @@ async function listSessionEvidenceSegments(input) {
|
|
|
6122
6285
|
projectKey,
|
|
6123
6286
|
sessionKey: "list"
|
|
6124
6287
|
});
|
|
6125
|
-
for (const sessionKey of await
|
|
6288
|
+
for (const sessionKey of await listDirectoryNames3(paths.projectDir)) {
|
|
6126
6289
|
const sessionPaths = resolveSessionMemoryPaths({
|
|
6127
6290
|
homeDir: input.homeDir,
|
|
6128
6291
|
projectKey,
|
|
@@ -6130,17 +6293,33 @@ async function listSessionEvidenceSegments(input) {
|
|
|
6130
6293
|
});
|
|
6131
6294
|
for (const entry of await listJsonFiles(sessionPaths.segmentsDir)) {
|
|
6132
6295
|
try {
|
|
6133
|
-
segments.push(parseSessionEvidenceSegment(JSON.parse(await
|
|
6296
|
+
segments.push(parseSessionEvidenceSegment(JSON.parse(await readFile8(entry, "utf8"))));
|
|
6134
6297
|
} catch {}
|
|
6135
6298
|
}
|
|
6136
6299
|
}
|
|
6137
6300
|
}
|
|
6138
6301
|
return segments.sort((left, right) => right.createdAt.localeCompare(left.createdAt));
|
|
6139
6302
|
}
|
|
6303
|
+
async function listSessionMemoryStates(input) {
|
|
6304
|
+
const rootDir = join10(resolveEvoDevPaths(input.homeDir).stateDir, "session-memory");
|
|
6305
|
+
const projectDirs = input.projectKey === undefined ? await listDirectoryNames3(rootDir) : [input.projectKey];
|
|
6306
|
+
const states = [];
|
|
6307
|
+
for (const projectKey of projectDirs) {
|
|
6308
|
+
const projectDir = join10(rootDir, projectKey);
|
|
6309
|
+
for (const sessionKey of await listDirectoryNames3(projectDir)) {
|
|
6310
|
+
try {
|
|
6311
|
+
const state = parseSessionMemoryState(JSON.parse(await readFile8(join10(projectDir, sessionKey, "state.json"), "utf8")));
|
|
6312
|
+
if (state.projectKey === projectKey && state.sessionKey === sessionKey)
|
|
6313
|
+
states.push(state);
|
|
6314
|
+
} catch {}
|
|
6315
|
+
}
|
|
6316
|
+
}
|
|
6317
|
+
return states.sort((left, right) => right.updatedAt.localeCompare(left.updatedAt) || left.sessionKey.localeCompare(right.sessionKey));
|
|
6318
|
+
}
|
|
6140
6319
|
async function readSessionEvidenceSegment(input) {
|
|
6141
6320
|
const paths = resolveSessionMemoryPaths(input);
|
|
6142
6321
|
try {
|
|
6143
|
-
const segment = parseSessionEvidenceSegment(JSON.parse(await
|
|
6322
|
+
const segment = parseSessionEvidenceSegment(JSON.parse(await readFile8(paths.segmentPath(input.segmentId), "utf8")));
|
|
6144
6323
|
if (segment.id !== input.segmentId || segment.projectKey !== input.projectKey || segment.sessionKey !== input.sessionKey) {
|
|
6145
6324
|
throw new SessionMemoryEvidenceError("invalid", "Session evidence identity does not match its storage path.");
|
|
6146
6325
|
}
|
|
@@ -6156,7 +6335,7 @@ async function readSessionEvidenceSegment(input) {
|
|
|
6156
6335
|
}
|
|
6157
6336
|
async function readSessionState(path) {
|
|
6158
6337
|
try {
|
|
6159
|
-
return JSON.parse(await
|
|
6338
|
+
return JSON.parse(await readFile8(path, "utf8"));
|
|
6160
6339
|
} catch (error) {
|
|
6161
6340
|
if (isNotFoundError(error))
|
|
6162
6341
|
return null;
|
|
@@ -6165,7 +6344,7 @@ async function readSessionState(path) {
|
|
|
6165
6344
|
}
|
|
6166
6345
|
async function readSessionCursor(path, sessionKey, sourcePath, now) {
|
|
6167
6346
|
try {
|
|
6168
|
-
return JSON.parse(await
|
|
6347
|
+
return JSON.parse(await readFile8(path, "utf8"));
|
|
6169
6348
|
} catch (error) {
|
|
6170
6349
|
if (!isNotFoundError(error))
|
|
6171
6350
|
throw error;
|
|
@@ -6184,13 +6363,13 @@ async function readSessionCursor(path, sessionKey, sourcePath, now) {
|
|
|
6184
6363
|
}
|
|
6185
6364
|
async function appendRawEvent(path, event) {
|
|
6186
6365
|
const existingLineCount = await countJsonlLines(path);
|
|
6187
|
-
await mkdir6(
|
|
6366
|
+
await mkdir6(dirname7(path), { recursive: true });
|
|
6188
6367
|
await appendFile2(path, `${JSON.stringify(event)}
|
|
6189
6368
|
`, "utf8");
|
|
6190
6369
|
return { lineNumber: existingLineCount + 1 };
|
|
6191
6370
|
}
|
|
6192
6371
|
async function readLineRange(path, fromLine, toLine) {
|
|
6193
|
-
const text2 = await
|
|
6372
|
+
const text2 = await readFile8(path, "utf8");
|
|
6194
6373
|
const lines = text2.split(`
|
|
6195
6374
|
`).filter((line) => line.trim() !== "");
|
|
6196
6375
|
const selected = lines.slice(Math.max(0, fromLine - 1), toLine);
|
|
@@ -6229,13 +6408,13 @@ async function writeSessionIndex(paths, state, segment, now) {
|
|
|
6229
6408
|
});
|
|
6230
6409
|
}
|
|
6231
6410
|
async function writeJson2(path, value) {
|
|
6232
|
-
await mkdir6(
|
|
6411
|
+
await mkdir6(dirname7(path), { recursive: true });
|
|
6233
6412
|
await writeFile5(path, `${JSON.stringify(value, null, 2)}
|
|
6234
6413
|
`, "utf8");
|
|
6235
6414
|
}
|
|
6236
6415
|
async function countJsonlLines(path) {
|
|
6237
6416
|
try {
|
|
6238
|
-
const text2 = await
|
|
6417
|
+
const text2 = await readFile8(path, "utf8");
|
|
6239
6418
|
return text2.split(`
|
|
6240
6419
|
`).filter((line) => line.trim() !== "").length;
|
|
6241
6420
|
} catch (error) {
|
|
@@ -6246,7 +6425,7 @@ async function countJsonlLines(path) {
|
|
|
6246
6425
|
}
|
|
6247
6426
|
async function readSessionIndex(path) {
|
|
6248
6427
|
try {
|
|
6249
|
-
const parsed = JSON.parse(await
|
|
6428
|
+
const parsed = JSON.parse(await readFile8(path, "utf8"));
|
|
6250
6429
|
if (!Array.isArray(parsed.segments))
|
|
6251
6430
|
return { segments: [] };
|
|
6252
6431
|
return {
|
|
@@ -6274,9 +6453,19 @@ function parseSessionEvidenceSegment(value) {
|
|
|
6274
6453
|
}
|
|
6275
6454
|
return segment;
|
|
6276
6455
|
}
|
|
6277
|
-
|
|
6456
|
+
function parseSessionMemoryState(value) {
|
|
6457
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
6458
|
+
throw new SessionMemoryEvidenceError("invalid", "Session Memory state must be an object.");
|
|
6459
|
+
}
|
|
6460
|
+
const state = value;
|
|
6461
|
+
if (state.schemaVersion !== 1 || state.kind !== "session-memory-state" || typeof state.projectKey !== "string" || typeof state.sessionKey !== "string" || !(state.runId === null || typeof state.runId === "string") || !(state.roleId === null || typeof state.roleId === "string") || typeof state.createdAt !== "string" || typeof state.updatedAt !== "string" || typeof state.counters !== "object" || state.counters === null) {
|
|
6462
|
+
throw new SessionMemoryEvidenceError("invalid", "Session Memory state fields are invalid.");
|
|
6463
|
+
}
|
|
6464
|
+
return state;
|
|
6465
|
+
}
|
|
6466
|
+
async function listDirectoryNames3(path) {
|
|
6278
6467
|
try {
|
|
6279
|
-
return (await
|
|
6468
|
+
return (await readdir8(path, { withFileTypes: true })).filter((entry) => entry.isDirectory()).map((entry) => entry.name).sort();
|
|
6280
6469
|
} catch (error) {
|
|
6281
6470
|
if (isNotFoundError(error))
|
|
6282
6471
|
return [];
|
|
@@ -6285,7 +6474,7 @@ async function listDirectoryNames2(path) {
|
|
|
6285
6474
|
}
|
|
6286
6475
|
async function listJsonFiles(path) {
|
|
6287
6476
|
try {
|
|
6288
|
-
return (await
|
|
6477
|
+
return (await readdir8(path, { withFileTypes: true })).filter((entry) => entry.isFile() && entry.name.endsWith(".json")).map((entry) => join10(path, entry.name)).sort();
|
|
6289
6478
|
} catch (error) {
|
|
6290
6479
|
if (isNotFoundError(error))
|
|
6291
6480
|
return [];
|
|
@@ -6293,7 +6482,7 @@ async function listJsonFiles(path) {
|
|
|
6293
6482
|
}
|
|
6294
6483
|
}
|
|
6295
6484
|
// packages/core/src/evolution/evidence/session-memory/updater.ts
|
|
6296
|
-
import { join as
|
|
6485
|
+
import { join as join12 } from "node:path";
|
|
6297
6486
|
|
|
6298
6487
|
// packages/core/src/evolution/triggers/index.ts
|
|
6299
6488
|
var exports_triggers = {};
|
|
@@ -6308,8 +6497,8 @@ __export(exports_triggers, {
|
|
|
6308
6497
|
enqueueSegmentEvolutionTrigger: () => enqueueSegmentEvolutionTrigger,
|
|
6309
6498
|
enqueueEvolutionTrigger: () => enqueueEvolutionTrigger
|
|
6310
6499
|
});
|
|
6311
|
-
import { readFile as
|
|
6312
|
-
import { join as
|
|
6500
|
+
import { readFile as readFile9 } from "node:fs/promises";
|
|
6501
|
+
import { join as join11 } from "node:path";
|
|
6313
6502
|
|
|
6314
6503
|
// packages/core/src/evolution/triggers/classification.ts
|
|
6315
6504
|
function normalizeEvolutionEventType(value) {
|
|
@@ -6444,19 +6633,19 @@ async function enqueueEvolutionTrigger(input) {
|
|
|
6444
6633
|
};
|
|
6445
6634
|
validateEvolutionTriggerRecord(trigger);
|
|
6446
6635
|
const paths = resolveEvolutionPaths({ homeDir: input.homeDir, projectKey, runId });
|
|
6447
|
-
await writeJsonFile(
|
|
6636
|
+
await writeJsonFile(join11(paths.triggersDir, `${trigger.id}.json`), trigger, { overwrite: false });
|
|
6448
6637
|
return trigger;
|
|
6449
6638
|
}
|
|
6450
6639
|
async function listEvolutionTriggers(input) {
|
|
6451
6640
|
const paths = resolveEvoDevPaths(input.homeDir);
|
|
6452
|
-
const evolutionStateDir =
|
|
6641
|
+
const evolutionStateDir = join11(paths.stateDir, "evolution");
|
|
6453
6642
|
const projectKeys = input.projectKey === undefined ? await listDirectoryNames(evolutionStateDir) : [sanitizeStorageId2("projectKey", input.projectKey)];
|
|
6454
6643
|
const triggers = [];
|
|
6455
6644
|
for (const projectKey of projectKeys) {
|
|
6456
|
-
const projectStateDir =
|
|
6645
|
+
const projectStateDir = join11(evolutionStateDir, projectKey);
|
|
6457
6646
|
const runIds = input.runId === undefined ? await listDirectoryNames(projectStateDir) : [sanitizeStorageId2("runId", input.runId)];
|
|
6458
6647
|
for (const runId of runIds) {
|
|
6459
|
-
const runTriggers = await readJsonFiles(
|
|
6648
|
+
const runTriggers = await readJsonFiles(join11(projectStateDir, runId, "triggers"), parseTrigger);
|
|
6460
6649
|
triggers.push(...runTriggers.filter((trigger) => input.status === undefined || trigger.status === input.status));
|
|
6461
6650
|
}
|
|
6462
6651
|
}
|
|
@@ -6497,16 +6686,16 @@ async function enqueueSegmentEvolutionTrigger(input) {
|
|
|
6497
6686
|
} catch (error) {
|
|
6498
6687
|
if (!isFileExistsError(error))
|
|
6499
6688
|
throw error;
|
|
6500
|
-
return parseSegmentTrigger(JSON.parse(await
|
|
6689
|
+
return parseSegmentTrigger(JSON.parse(await readFile9(path, "utf8")));
|
|
6501
6690
|
}
|
|
6502
6691
|
}
|
|
6503
6692
|
async function listSegmentEvolutionTriggers(input) {
|
|
6504
6693
|
const paths = resolveEvoDevPaths(input.homeDir);
|
|
6505
|
-
const evolutionStateDir =
|
|
6694
|
+
const evolutionStateDir = join11(paths.stateDir, "evolution");
|
|
6506
6695
|
const projectKeys = input.projectKey === undefined ? await listDirectoryNames(evolutionStateDir) : [sanitizeStorageId2("projectKey", input.projectKey)];
|
|
6507
6696
|
const triggers = [];
|
|
6508
6697
|
for (const projectKey of projectKeys) {
|
|
6509
|
-
const projectSegmentsDir =
|
|
6698
|
+
const projectSegmentsDir = join11(evolutionStateDir, projectKey, "segments");
|
|
6510
6699
|
const projectTriggers = await readJsonFiles(projectSegmentsDir, parseSegmentTrigger);
|
|
6511
6700
|
triggers.push(...projectTriggers.filter((trigger) => {
|
|
6512
6701
|
if (input.status !== undefined && trigger.status !== input.status)
|
|
@@ -6543,7 +6732,7 @@ async function updateTriggers(homeDir, triggers, patch) {
|
|
|
6543
6732
|
projectKey: next.projectKey,
|
|
6544
6733
|
runId: next.runId
|
|
6545
6734
|
});
|
|
6546
|
-
await writeJsonFile(
|
|
6735
|
+
await writeJsonFile(join11(paths.triggersDir, `${next.id}.json`), next, { overwrite: true });
|
|
6547
6736
|
}
|
|
6548
6737
|
}
|
|
6549
6738
|
async function updateSegmentTriggers(homeDir, triggers, patch) {
|
|
@@ -6562,7 +6751,7 @@ async function updateSegmentTriggers(homeDir, triggers, patch) {
|
|
|
6562
6751
|
}
|
|
6563
6752
|
function resolveSegmentEvolutionTriggerPath(homeDir, trigger) {
|
|
6564
6753
|
const paths = resolveEvoDevPaths(homeDir);
|
|
6565
|
-
return
|
|
6754
|
+
return join11(paths.stateDir, "evolution", sanitizeStorageId2("projectKey", trigger.projectKey), "segments", `${sanitizeStorageId2("segmentTrigger", trigger.id)}.json`);
|
|
6566
6755
|
}
|
|
6567
6756
|
|
|
6568
6757
|
// packages/core/src/evolution/evidence/session-memory/segment.ts
|
|
@@ -6877,7 +7066,9 @@ async function updateSessionMemoryFromHook(input) {
|
|
|
6877
7066
|
environment: input.environment,
|
|
6878
7067
|
payload: input.rawPayload
|
|
6879
7068
|
});
|
|
6880
|
-
const
|
|
7069
|
+
const cwd = optionalString(input.rawPayload.cwd);
|
|
7070
|
+
const workspace = team === null && cwd !== null ? await resolveProjectWorkspaceFromCwd({ homeDir: input.homeDir, cwd }) : null;
|
|
7071
|
+
const projectKey = sanitizeStorageId(team?.projectKey ?? workspace?.projectKey ?? resolveProjectLogKey(input.homeDir, cwd ?? input.homeDir), "project");
|
|
6881
7072
|
const sessionKey = sanitizeStorageId(resolveTraceSessionKey(input.rawPayload), "session");
|
|
6882
7073
|
const paths = resolveSessionMemoryPaths({ homeDir: input.homeDir, projectKey, sessionKey });
|
|
6883
7074
|
const now = normalizeTimestamp(input.receivedAt ?? input.event.time.receivedAt);
|
|
@@ -6971,7 +7162,7 @@ async function updateSessionMemoryFromHook(input) {
|
|
|
6971
7162
|
await writeJson2(paths.cursorPath, cursor);
|
|
6972
7163
|
stateWrites.push(paths.statePath, paths.cursorPath);
|
|
6973
7164
|
if (queuedSegmentTriggerId !== null) {
|
|
6974
|
-
stateWrites.push(
|
|
7165
|
+
stateWrites.push(join12(input.homeDir, ".evodev", "state", "evolution", projectKey, "segments", `${queuedSegmentTriggerId}.json`));
|
|
6975
7166
|
}
|
|
6976
7167
|
return {
|
|
6977
7168
|
state,
|
|
@@ -6993,577 +7184,13 @@ function emptyResult() {
|
|
|
6993
7184
|
};
|
|
6994
7185
|
}
|
|
6995
7186
|
// packages/core/src/hooks/index.ts
|
|
6996
|
-
import { mkdir as
|
|
7187
|
+
import { mkdir as mkdir8, readFile as readFile11, writeFile as writeFile7 } from "node:fs/promises";
|
|
6997
7188
|
import { dirname as dirname9, join as join14 } from "node:path";
|
|
6998
7189
|
|
|
6999
|
-
// packages/core/src/task/index.ts
|
|
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";
|
|
7002
|
-
var FORBIDDEN_TASK_PATHS = ["CLAUDE.md", "AGENTS.md", ".claude/**", ".codex/**"];
|
|
7003
|
-
var FORBIDDEN_TASK_WRITE_SEGMENTS = new Set([".claude", ".codex"]);
|
|
7004
|
-
var FORBIDDEN_PROJECT_ASSET_SEGMENTS = new Set(["packages", "src"]);
|
|
7005
|
-
var FORBIDDEN_TASK_WRITE_FILES = new Set(["agents.md", "claude.md", "package.json", "readme.md"]);
|
|
7006
|
-
var FORBIDDEN_RAW_KEYS2 = new Set([
|
|
7007
|
-
"rawoutput",
|
|
7008
|
-
"raw_output",
|
|
7009
|
-
"stdout",
|
|
7010
|
-
"stderr",
|
|
7011
|
-
"source",
|
|
7012
|
-
"sourcecontent",
|
|
7013
|
-
"source_content",
|
|
7014
|
-
"sourcetext",
|
|
7015
|
-
"source_text",
|
|
7016
|
-
"prompt",
|
|
7017
|
-
"prompttext",
|
|
7018
|
-
"prompt_text",
|
|
7019
|
-
"transcript",
|
|
7020
|
-
"transcripttext",
|
|
7021
|
-
"transcript_text",
|
|
7022
|
-
"secret",
|
|
7023
|
-
"secretvalue",
|
|
7024
|
-
"secret_value"
|
|
7025
|
-
]);
|
|
7026
|
-
var ALLOWED_VERIFICATION_KEYS = new Set([
|
|
7027
|
-
"acceptanceResults",
|
|
7028
|
-
"antiCriteriaResults",
|
|
7029
|
-
"commands",
|
|
7030
|
-
"evidence",
|
|
7031
|
-
"exitCode",
|
|
7032
|
-
"id",
|
|
7033
|
-
"status",
|
|
7034
|
-
"summary",
|
|
7035
|
-
"type"
|
|
7036
|
-
]);
|
|
7037
|
-
var SENSITIVE_TEXT_PATTERN3 = /https?:\/\/\S+|\b(secret|token|password|passwd|private|internal|api[_-]?key|apikey|credential|credentials|raw log|raw source|raw prompt)\b/gi;
|
|
7038
|
-
function createTaskContract(input) {
|
|
7039
|
-
const taskId = createTaskId(input.title);
|
|
7040
|
-
const summary = input.summary ?? input.title;
|
|
7041
|
-
const acceptanceCriteria = input.acceptanceCriteria === undefined || input.acceptanceCriteria.length === 0 ? [
|
|
7042
|
-
{
|
|
7043
|
-
id: "AC1",
|
|
7044
|
-
category: "engineering",
|
|
7045
|
-
statement: "Task outcome satisfies the requested target state.",
|
|
7046
|
-
requiredEvidence: ["verification-summary"],
|
|
7047
|
-
status: "not-run"
|
|
7048
|
-
}
|
|
7049
|
-
] : input.acceptanceCriteria.map((criterion) => ({
|
|
7050
|
-
id: sanitizeId2(criterion.id),
|
|
7051
|
-
category: criterion.category,
|
|
7052
|
-
statement: sanitizeText2(criterion.statement),
|
|
7053
|
-
requiredEvidence: uniqueSanitizedIds2(criterion.requiredEvidence ?? []),
|
|
7054
|
-
status: "not-run"
|
|
7055
|
-
}));
|
|
7056
|
-
const defaultAntiCriteria = [
|
|
7057
|
-
{
|
|
7058
|
-
id: "ANTI1",
|
|
7059
|
-
category: "privacy",
|
|
7060
|
-
statement: "Do not collect source code, prompts, raw command output, secrets, or internal links.",
|
|
7061
|
-
status: "unknown"
|
|
7062
|
-
},
|
|
7063
|
-
{
|
|
7064
|
-
id: "ANTI2",
|
|
7065
|
-
category: "scope",
|
|
7066
|
-
statement: "Do not write outside approved task storage or approved implementation scope.",
|
|
7067
|
-
status: "unknown"
|
|
7068
|
-
}
|
|
7069
|
-
];
|
|
7070
|
-
return {
|
|
7071
|
-
version: 1,
|
|
7072
|
-
taskId,
|
|
7073
|
-
status: "draft",
|
|
7074
|
-
classification: "local-private",
|
|
7075
|
-
source: { summary: sanitizeText2(summary), rawPromptStored: false },
|
|
7076
|
-
context: {
|
|
7077
|
-
projectId: input.projectId === undefined || input.projectId === null ? null : sanitizeText2(input.projectId),
|
|
7078
|
-
relatedFiles: [],
|
|
7079
|
-
assumptions: [],
|
|
7080
|
-
openQuestions: []
|
|
7081
|
-
},
|
|
7082
|
-
currentState: { summary: "To be completed by the task owner.", evidenceRefs: [] },
|
|
7083
|
-
targetState: { summary: sanitizeText2(input.title), nonGoals: [], constraints: [] },
|
|
7084
|
-
scope: {
|
|
7085
|
-
allowedPaths: sanitizeTextList(input.allowedPaths ?? []),
|
|
7086
|
-
forbiddenPaths: FORBIDDEN_TASK_PATHS,
|
|
7087
|
-
allowedOperations: input.allowedOperations === undefined || input.allowedOperations.length === 0 ? ["read", "edit-approved-files", "run-local-tests"] : sanitizeTextList(input.allowedOperations),
|
|
7088
|
-
requiresUserConfirmation: sanitizeTextList(input.requiresUserConfirmation ?? [])
|
|
7089
|
-
},
|
|
7090
|
-
acceptanceCriteria,
|
|
7091
|
-
antiCriteria: [
|
|
7092
|
-
...defaultAntiCriteria,
|
|
7093
|
-
...(input.antiCriteria ?? []).map((criterion) => ({
|
|
7094
|
-
id: sanitizeId2(criterion.id),
|
|
7095
|
-
category: criterion.category,
|
|
7096
|
-
statement: sanitizeText2(criterion.statement),
|
|
7097
|
-
status: "unknown"
|
|
7098
|
-
}))
|
|
7099
|
-
],
|
|
7100
|
-
route: {
|
|
7101
|
-
mode: null,
|
|
7102
|
-
workflowId: null,
|
|
7103
|
-
rationale: "Not routed yet.",
|
|
7104
|
-
requiredReview: [],
|
|
7105
|
-
requiredVerification: uniqueSanitizedIds2(input.requiredVerification ?? [])
|
|
7106
|
-
},
|
|
7107
|
-
verification: {
|
|
7108
|
-
policy: "advisory",
|
|
7109
|
-
commands: [],
|
|
7110
|
-
acceptanceResults: [],
|
|
7111
|
-
antiCriteriaResults: [],
|
|
7112
|
-
status: "not-run",
|
|
7113
|
-
summary: "Verification has not run."
|
|
7114
|
-
},
|
|
7115
|
-
evidence: { metadataOnly: true, items: [] },
|
|
7116
|
-
learningCandidates: []
|
|
7117
|
-
};
|
|
7118
|
-
}
|
|
7119
|
-
function routeTaskContract(contract) {
|
|
7120
|
-
const mode = selectMode(contract);
|
|
7121
|
-
const requiredVerification = uniqueSanitizedIds2([
|
|
7122
|
-
...contract.route.requiredVerification ?? [],
|
|
7123
|
-
"verification-summary"
|
|
7124
|
-
]);
|
|
7125
|
-
return {
|
|
7126
|
-
...contract,
|
|
7127
|
-
status: "routed",
|
|
7128
|
-
route: {
|
|
7129
|
-
mode,
|
|
7130
|
-
workflowId: selectWorkflowId(contract, mode),
|
|
7131
|
-
rationale: createRouteRationale(contract, mode),
|
|
7132
|
-
requiredReview: mode === "rigorous" ? ["security-boundary", "verification"] : [],
|
|
7133
|
-
requiredVerification
|
|
7134
|
-
}
|
|
7135
|
-
};
|
|
7136
|
-
}
|
|
7137
|
-
function verifyTaskContract(contract, input) {
|
|
7138
|
-
assertMetadataOnly(input);
|
|
7139
|
-
const commands = (input.commands ?? []).map((command) => ({
|
|
7140
|
-
id: sanitizeId2(command.id),
|
|
7141
|
-
status: assertVerificationCommandStatus(command.status, `command ${command.id}`),
|
|
7142
|
-
exitCode: command.exitCode,
|
|
7143
|
-
summary: sanitizeText2(command.summary ?? `${command.id}: ${command.status}`),
|
|
7144
|
-
rawOutputStored: false
|
|
7145
|
-
}));
|
|
7146
|
-
const acceptanceResults = (input.acceptanceResults ?? []).map((result) => ({
|
|
7147
|
-
id: sanitizeId2(result.id),
|
|
7148
|
-
status: assertAcceptanceResultStatus(result.status, `acceptance ${result.id}`),
|
|
7149
|
-
summary: sanitizeText2(result.summary ?? `${result.id}: ${result.status}`)
|
|
7150
|
-
}));
|
|
7151
|
-
const antiCriteriaResults = (input.antiCriteriaResults ?? []).map((result) => ({
|
|
7152
|
-
id: sanitizeId2(result.id),
|
|
7153
|
-
status: assertAntiCriteriaResultStatus(result.status, `anti-criteria ${result.id}`),
|
|
7154
|
-
summary: sanitizeText2(result.summary ?? `${result.id}: ${result.status}`)
|
|
7155
|
-
}));
|
|
7156
|
-
const evidence = (input.evidence ?? []).map((item) => ({
|
|
7157
|
-
type: assertEvidenceType(item.type, `evidence ${item.id}`),
|
|
7158
|
-
id: sanitizeId2(item.id),
|
|
7159
|
-
status: assertVerificationStatus(item.status, `evidence ${item.id}`),
|
|
7160
|
-
summary: sanitizeText2(item.summary ?? `${item.id}: ${item.status}`),
|
|
7161
|
-
rawOutputStored: false,
|
|
7162
|
-
sourceContentStored: false
|
|
7163
|
-
}));
|
|
7164
|
-
const failures = collectVerificationFailures(contract, commands, acceptanceResults, antiCriteriaResults, evidence);
|
|
7165
|
-
const ok = failures.length === 0;
|
|
7166
|
-
const summary = ok ? "Verification passed." : `Verification failed: ${failures.join("; ")}`;
|
|
7167
|
-
return {
|
|
7168
|
-
ok,
|
|
7169
|
-
summary,
|
|
7170
|
-
contract: {
|
|
7171
|
-
...contract,
|
|
7172
|
-
status: ok ? "verified" : "failed",
|
|
7173
|
-
acceptanceCriteria: contract.acceptanceCriteria.map((criterion) => ({
|
|
7174
|
-
...criterion,
|
|
7175
|
-
status: acceptanceResults.find((result) => result.id === criterion.id)?.status ?? criterion.status
|
|
7176
|
-
})),
|
|
7177
|
-
antiCriteria: contract.antiCriteria.map((criterion) => ({
|
|
7178
|
-
...criterion,
|
|
7179
|
-
status: antiCriteriaResults.find((result) => result.id === criterion.id)?.status ?? criterion.status
|
|
7180
|
-
})),
|
|
7181
|
-
verification: {
|
|
7182
|
-
policy: "advisory",
|
|
7183
|
-
commands,
|
|
7184
|
-
acceptanceResults,
|
|
7185
|
-
antiCriteriaResults,
|
|
7186
|
-
status: ok ? "pass" : "fail",
|
|
7187
|
-
summary
|
|
7188
|
-
},
|
|
7189
|
-
evidence: {
|
|
7190
|
-
metadataOnly: true,
|
|
7191
|
-
items: evidence
|
|
7192
|
-
}
|
|
7193
|
-
}
|
|
7194
|
-
};
|
|
7195
|
-
}
|
|
7196
|
-
async function readTaskContract(path) {
|
|
7197
|
-
return parseTaskContract(JSON.parse(await readFile9(path, "utf8")));
|
|
7198
|
-
}
|
|
7199
|
-
async function writeTaskContract(path, contract, options = {}) {
|
|
7200
|
-
await assertTaskContractWritePathAllowed(path);
|
|
7201
|
-
await mkdir7(dirname7(path), { recursive: true });
|
|
7202
|
-
await writeFile6(path, `${JSON.stringify(contract, null, 2)}
|
|
7203
|
-
`, {
|
|
7204
|
-
encoding: "utf8",
|
|
7205
|
-
flag: options.overwrite === true ? "w" : "wx"
|
|
7206
|
-
});
|
|
7207
|
-
}
|
|
7208
|
-
async function resolveTaskContractOutputPath(input) {
|
|
7209
|
-
if (input.outputDir !== undefined) {
|
|
7210
|
-
const outputPath = join12(input.outputDir, input.taskId, "contract.json");
|
|
7211
|
-
await assertTaskContractWritePathAllowed(outputPath);
|
|
7212
|
-
return outputPath;
|
|
7213
|
-
}
|
|
7214
|
-
if (input.projectDir !== undefined) {
|
|
7215
|
-
const projectContextPath = join12(input.projectDir, ".evodev", "project.json");
|
|
7216
|
-
if (!await pathExists4(projectContextPath)) {
|
|
7217
|
-
throw new Error("Project mode requires existing .evodev/project.json; use --output-dir instead.");
|
|
7218
|
-
}
|
|
7219
|
-
const outputPath = join12(input.projectDir, ".evodev", "tasks", input.taskId, "contract.json");
|
|
7220
|
-
await assertTaskContractWritePathAllowed(outputPath);
|
|
7221
|
-
return outputPath;
|
|
7222
|
-
}
|
|
7223
|
-
throw new Error("Task writes require --output-dir or --project-dir with existing project context.");
|
|
7224
|
-
}
|
|
7225
|
-
function formatTaskContract(contract) {
|
|
7226
|
-
return [
|
|
7227
|
-
"EvoDev task contract",
|
|
7228
|
-
"",
|
|
7229
|
-
`Task id: ${contract.taskId}`,
|
|
7230
|
-
`Status: ${contract.status}`,
|
|
7231
|
-
`Summary: ${contract.source.summary}`,
|
|
7232
|
-
`Mode: ${contract.route.mode ?? "not routed"}`,
|
|
7233
|
-
`Workflow: ${contract.route.workflowId ?? "not routed"}`,
|
|
7234
|
-
`Required verification: ${formatList(contract.route.requiredVerification)}`,
|
|
7235
|
-
`Required review: ${formatList(contract.route.requiredReview)}`,
|
|
7236
|
-
`Route rationale: ${contract.route.rationale}`,
|
|
7237
|
-
`Verification: ${contract.verification.status}`,
|
|
7238
|
-
`Verification summary: ${contract.verification.summary}`
|
|
7239
|
-
].join(`
|
|
7240
|
-
`);
|
|
7241
|
-
}
|
|
7242
|
-
function selectMode(contract) {
|
|
7243
|
-
if (selectRigorousTrigger(contract) !== null) {
|
|
7244
|
-
return "rigorous";
|
|
7245
|
-
}
|
|
7246
|
-
const allowedPaths = contract.scope.allowedPaths ?? [];
|
|
7247
|
-
if (allowedPaths.length <= 1 && contract.acceptanceCriteria.length <= 1) {
|
|
7248
|
-
return "minimal";
|
|
7249
|
-
}
|
|
7250
|
-
return "standard";
|
|
7251
|
-
}
|
|
7252
|
-
function selectWorkflowId(contract, mode) {
|
|
7253
|
-
const text2 = collectRouteText(contract);
|
|
7254
|
-
if (text2.includes("release") || text2.includes("publish"))
|
|
7255
|
-
return "rd-release-readiness";
|
|
7256
|
-
if (hasSecurityBoundaryTerms(text2) || mode === "rigorous") {
|
|
7257
|
-
return "rd-security-boundary-review";
|
|
7258
|
-
}
|
|
7259
|
-
if (text2.includes("bug"))
|
|
7260
|
-
return "rd-bug-fix";
|
|
7261
|
-
if (text2.includes("refactor"))
|
|
7262
|
-
return "rd-refactor";
|
|
7263
|
-
if (text2.includes("review"))
|
|
7264
|
-
return "rd-code-review";
|
|
7265
|
-
if (text2.includes("doc"))
|
|
7266
|
-
return "rd-docs-update";
|
|
7267
|
-
if (text2.includes("test"))
|
|
7268
|
-
return "rd-test-generation";
|
|
7269
|
-
if (mode === "minimal")
|
|
7270
|
-
return "rd-docs-update";
|
|
7271
|
-
return "rd-feature-implementation";
|
|
7272
|
-
}
|
|
7273
|
-
function collectRouteText(contract) {
|
|
7274
|
-
return [
|
|
7275
|
-
contract.taskId,
|
|
7276
|
-
contract.source.summary,
|
|
7277
|
-
contract.currentState.summary,
|
|
7278
|
-
...contract.currentState.evidenceRefs,
|
|
7279
|
-
contract.targetState.summary,
|
|
7280
|
-
...contract.targetState.nonGoals,
|
|
7281
|
-
...contract.targetState.constraints,
|
|
7282
|
-
...contract.context.relatedFiles.map((file) => `${file.path} ${file.reason}`),
|
|
7283
|
-
...contract.context.assumptions,
|
|
7284
|
-
...contract.context.openQuestions,
|
|
7285
|
-
...contract.scope.allowedPaths ?? [],
|
|
7286
|
-
...contract.scope.allowedOperations ?? [],
|
|
7287
|
-
...contract.scope.requiresUserConfirmation ?? [],
|
|
7288
|
-
...contract.acceptanceCriteria.map((criterion) => criterion.statement),
|
|
7289
|
-
...contract.acceptanceCriteria.flatMap((criterion) => criterion.requiredEvidence),
|
|
7290
|
-
...contract.route.requiredVerification ?? []
|
|
7291
|
-
].join(" ").toLowerCase();
|
|
7292
|
-
}
|
|
7293
|
-
function createRouteRationale(contract, mode) {
|
|
7294
|
-
const trigger = selectRigorousTrigger(contract);
|
|
7295
|
-
if (mode === "rigorous")
|
|
7296
|
-
return `Selected rigorous due to ${trigger ?? "high-risk"} trigger.`;
|
|
7297
|
-
if (mode === "minimal")
|
|
7298
|
-
return "Selected minimal for narrow scope and simple acceptance criteria.";
|
|
7299
|
-
return "Selected standard for bounded engineering work requiring Task Contract verification.";
|
|
7300
|
-
}
|
|
7301
|
-
function selectRigorousTrigger(contract) {
|
|
7302
|
-
const text2 = collectRouteText(contract);
|
|
7303
|
-
if (text2.includes("[redacted]") || hasSecurityBoundaryTerms(text2))
|
|
7304
|
-
return "privacy/security";
|
|
7305
|
-
if (/\b(release|publish|publishing|package distribution|npm publish)\b/.test(text2)) {
|
|
7306
|
-
return "release/publish";
|
|
7307
|
-
}
|
|
7308
|
-
if (/\b(hook|hooks|learning|memory|telemetry|observability)\b/.test(text2)) {
|
|
7309
|
-
return "hook/learning/telemetry";
|
|
7310
|
-
}
|
|
7311
|
-
if (/\b(migration|migrate|hard to rollback|hard-to-rollback|irreversible|destructive)\b/.test(text2)) {
|
|
7312
|
-
return "hard-to-rollback";
|
|
7313
|
-
}
|
|
7314
|
-
if (hasHighRiskOperationTerms(text2)) {
|
|
7315
|
-
return "high-risk operation";
|
|
7316
|
-
}
|
|
7317
|
-
if ((contract.scope.requiresUserConfirmation ?? []).length > 0) {
|
|
7318
|
-
return "explicit user confirmation";
|
|
7319
|
-
}
|
|
7320
|
-
for (const path of contract.scope.allowedPaths ?? []) {
|
|
7321
|
-
const trigger = selectRigorousPathTrigger(path);
|
|
7322
|
-
if (trigger !== null)
|
|
7323
|
-
return trigger;
|
|
7324
|
-
}
|
|
7325
|
-
for (const operation of contract.scope.allowedOperations ?? []) {
|
|
7326
|
-
if (hasHighRiskOperationTerms(operation))
|
|
7327
|
-
return "high-risk operation";
|
|
7328
|
-
}
|
|
7329
|
-
return null;
|
|
7330
|
-
}
|
|
7331
|
-
function hasSecurityBoundaryTerms(text2) {
|
|
7332
|
-
return /\b(security|privacy|private data|auth|authentication|authorization|credential|credentials|secret|secrets|token|tokens|api key|api-key|apikey|password|passwd)\b/.test(text2);
|
|
7333
|
-
}
|
|
7334
|
-
function selectRigorousPathTrigger(path) {
|
|
7335
|
-
const normalized = path.trim().replace(/\\/g, "/").toLowerCase();
|
|
7336
|
-
if (/^~\/\.(evodev|claude|codex)(\/|$)/.test(normalized)) {
|
|
7337
|
-
return "user-level Code Agent/EvoDev path";
|
|
7338
|
-
}
|
|
7339
|
-
if (normalized === ".evodev" || normalized.startsWith(".evodev/") || normalized.includes("/.evodev/")) {
|
|
7340
|
-
return "project .evodev path";
|
|
7341
|
-
}
|
|
7342
|
-
if (normalized === ".claude" || normalized.startsWith(".claude/") || normalized.includes("/.claude/") || normalized === ".codex" || normalized.startsWith(".codex/") || normalized.includes("/.codex/")) {
|
|
7343
|
-
return "project Code Agent config path";
|
|
7344
|
-
}
|
|
7345
|
-
const fileName = basename(normalized);
|
|
7346
|
-
if (fileName === "claude.md" || fileName === "agents.md")
|
|
7347
|
-
return "agent instruction file";
|
|
7348
|
-
return null;
|
|
7349
|
-
}
|
|
7350
|
-
function hasHighRiskOperationTerms(text2) {
|
|
7351
|
-
const normalized = text2.toLowerCase().replace(/[_-]+/g, " ");
|
|
7352
|
-
return /\b(delete|remove|publish|release|install|uninstall|network|external|external service|external api|write user config|user config|write project context|project context|hook|learning|memory|telemetry)\b/.test(normalized);
|
|
7353
|
-
}
|
|
7354
|
-
function collectVerificationFailures(contract, commands, acceptanceResults, antiCriteriaResults, evidence) {
|
|
7355
|
-
const failures = [];
|
|
7356
|
-
if (!isVerificationReady(contract)) {
|
|
7357
|
-
failures.push("task contract must be routed before verification");
|
|
7358
|
-
}
|
|
7359
|
-
failures.push(...collectDuplicateIdFailures("command", commands));
|
|
7360
|
-
failures.push(...collectDuplicateIdFailures("evidence", evidence));
|
|
7361
|
-
failures.push(...collectDuplicateIdFailures("acceptance result", acceptanceResults));
|
|
7362
|
-
failures.push(...collectDuplicateIdFailures("anti-criteria result", antiCriteriaResults));
|
|
7363
|
-
for (const requiredId of contract.route.requiredVerification ?? []) {
|
|
7364
|
-
const matches = [
|
|
7365
|
-
...commands.filter((command) => command.id === requiredId),
|
|
7366
|
-
...evidence.filter((item) => item.id === requiredId)
|
|
7367
|
-
];
|
|
7368
|
-
if (matches.length === 0) {
|
|
7369
|
-
failures.push(`missing required verification: ${requiredId}`);
|
|
7370
|
-
continue;
|
|
7371
|
-
}
|
|
7372
|
-
if (matches.some((item) => !isSatisfyingVerificationItem(item))) {
|
|
7373
|
-
failures.push(`required verification ${requiredId} is not satisfied`);
|
|
7374
|
-
}
|
|
7375
|
-
}
|
|
7376
|
-
for (const criterion of contract.acceptanceCriteria) {
|
|
7377
|
-
const result = acceptanceResults.find((candidate) => candidate.id === criterion.id);
|
|
7378
|
-
if (result === undefined || result.status !== "pass" && result.status !== "not-applicable") {
|
|
7379
|
-
failures.push(`acceptance ${criterion.id} is not satisfied`);
|
|
7380
|
-
}
|
|
7381
|
-
for (const evidenceId of criterion.requiredEvidence) {
|
|
7382
|
-
const matches = evidence.filter((item) => item.id === evidenceId);
|
|
7383
|
-
if (matches.length === 0) {
|
|
7384
|
-
failures.push(`missing evidence for ${criterion.id}: ${evidenceId}`);
|
|
7385
|
-
continue;
|
|
7386
|
-
}
|
|
7387
|
-
if (matches.some((item) => !isSatisfyingEvidenceItem(item))) {
|
|
7388
|
-
failures.push(`evidence for ${criterion.id} is not satisfied: ${evidenceId}`);
|
|
7389
|
-
}
|
|
7390
|
-
}
|
|
7391
|
-
}
|
|
7392
|
-
for (const criterion of contract.antiCriteria) {
|
|
7393
|
-
const result = antiCriteriaResults.find((candidate) => candidate.id === criterion.id);
|
|
7394
|
-
if (result === undefined || result.status === "unknown" || result.status === "triggered") {
|
|
7395
|
-
failures.push(`anti-criteria ${criterion.id} is not clear`);
|
|
7396
|
-
}
|
|
7397
|
-
}
|
|
7398
|
-
return failures;
|
|
7399
|
-
}
|
|
7400
|
-
function collectDuplicateIdFailures(label, items) {
|
|
7401
|
-
const seen = new Set;
|
|
7402
|
-
const duplicates = new Set;
|
|
7403
|
-
for (const item of items) {
|
|
7404
|
-
if (seen.has(item.id))
|
|
7405
|
-
duplicates.add(item.id);
|
|
7406
|
-
seen.add(item.id);
|
|
7407
|
-
}
|
|
7408
|
-
return Array.from(duplicates).map((id) => `duplicate ${label} id: ${id}`);
|
|
7409
|
-
}
|
|
7410
|
-
function isSatisfyingVerificationItem(item) {
|
|
7411
|
-
if ("rawOutputStored" in item && "type" in item)
|
|
7412
|
-
return isSatisfyingEvidenceItem(item);
|
|
7413
|
-
return item.status === "pass";
|
|
7414
|
-
}
|
|
7415
|
-
function isSatisfyingEvidenceItem(item) {
|
|
7416
|
-
if (item.type === "command-result")
|
|
7417
|
-
return item.status === "pass";
|
|
7418
|
-
return item.status === "pass" || item.status === "clear";
|
|
7419
|
-
}
|
|
7420
|
-
function isVerificationReady(contract) {
|
|
7421
|
-
return contract.status !== "draft" && contract.route.mode !== null && contract.route.workflowId !== null && (contract.route.requiredVerification ?? []).length > 0;
|
|
7422
|
-
}
|
|
7423
|
-
function parseTaskContract(value) {
|
|
7424
|
-
if (!isRecord7(value) || value.version !== 1 || typeof value.taskId !== "string") {
|
|
7425
|
-
throw new Error("Invalid Task Contract JSON.");
|
|
7426
|
-
}
|
|
7427
|
-
return value;
|
|
7428
|
-
}
|
|
7429
|
-
function assertMetadataOnly(value) {
|
|
7430
|
-
if (Array.isArray(value)) {
|
|
7431
|
-
for (const item of value)
|
|
7432
|
-
assertMetadataOnly(item);
|
|
7433
|
-
return;
|
|
7434
|
-
}
|
|
7435
|
-
if (!isRecord7(value)) {
|
|
7436
|
-
if (typeof value === "string" && containsSensitiveText(value)) {
|
|
7437
|
-
throw new Error("Verification input contains sensitive content in metadata field.");
|
|
7438
|
-
}
|
|
7439
|
-
return;
|
|
7440
|
-
}
|
|
7441
|
-
for (const [key, child] of Object.entries(value)) {
|
|
7442
|
-
const normalizedKey = key.toLowerCase().replace(/[^a-z0-9]/g, "");
|
|
7443
|
-
if (FORBIDDEN_RAW_KEYS2.has(key) || FORBIDDEN_RAW_KEYS2.has(normalizedKey)) {
|
|
7444
|
-
throw new Error(`Verification input contains forbidden raw field: ${key}`);
|
|
7445
|
-
}
|
|
7446
|
-
if (!ALLOWED_VERIFICATION_KEYS.has(key)) {
|
|
7447
|
-
throw new Error(`Verification input contains unsupported field: ${key}`);
|
|
7448
|
-
}
|
|
7449
|
-
assertMetadataOnly(child);
|
|
7450
|
-
}
|
|
7451
|
-
}
|
|
7452
|
-
function assertVerificationCommandStatus(value, label) {
|
|
7453
|
-
if (value === "pass" || value === "fail" || value === "not-run")
|
|
7454
|
-
return value;
|
|
7455
|
-
throw new Error(`Invalid verification command status for ${label}: ${String(value)}`);
|
|
7456
|
-
}
|
|
7457
|
-
function assertAcceptanceResultStatus(value, label) {
|
|
7458
|
-
if (value === "pass" || value === "fail" || value === "not-run" || value === "not-applicable") {
|
|
7459
|
-
return value;
|
|
7460
|
-
}
|
|
7461
|
-
throw new Error(`Invalid acceptance result status for ${label}: ${String(value)}`);
|
|
7462
|
-
}
|
|
7463
|
-
function assertAntiCriteriaResultStatus(value, label) {
|
|
7464
|
-
if (value === "clear" || value === "triggered" || value === "unknown")
|
|
7465
|
-
return value;
|
|
7466
|
-
throw new Error(`Invalid anti-criteria result status for ${label}: ${String(value)}`);
|
|
7467
|
-
}
|
|
7468
|
-
function assertVerificationStatus(value, label) {
|
|
7469
|
-
if (value === "pass" || value === "fail" || value === "not-run" || value === "not-applicable" || value === "unknown" || value === "triggered" || value === "clear") {
|
|
7470
|
-
return value;
|
|
7471
|
-
}
|
|
7472
|
-
throw new Error(`Invalid verification status for ${label}: ${String(value)}`);
|
|
7473
|
-
}
|
|
7474
|
-
function assertEvidenceType(value, label) {
|
|
7475
|
-
if (value === "command-result" || value === "manual-check" || value === "review")
|
|
7476
|
-
return value;
|
|
7477
|
-
throw new Error(`Invalid evidence type for ${label}: ${String(value)}`);
|
|
7478
|
-
}
|
|
7479
|
-
function sanitizeText2(value) {
|
|
7480
|
-
return value.replace(SENSITIVE_TEXT_PATTERN3, "[redacted]").slice(0, 500);
|
|
7481
|
-
}
|
|
7482
|
-
function containsSensitiveText(value) {
|
|
7483
|
-
SENSITIVE_TEXT_PATTERN3.lastIndex = 0;
|
|
7484
|
-
return SENSITIVE_TEXT_PATTERN3.test(value);
|
|
7485
|
-
}
|
|
7486
|
-
async function assertTaskContractWritePathAllowed(path) {
|
|
7487
|
-
assertTaskContractWritePathSegmentsAllowed(resolve3(path));
|
|
7488
|
-
assertTaskContractWritePathSegmentsAllowed(await resolveTaskWriteRealPath(path));
|
|
7489
|
-
}
|
|
7490
|
-
function assertTaskContractWritePathSegmentsAllowed(path) {
|
|
7491
|
-
const segments = path.replace(/\\/g, "/").split("/").filter((segment) => segment.length > 0).map((segment) => segment.toLowerCase());
|
|
7492
|
-
const protectedSegment = segments.find((segment) => FORBIDDEN_TASK_WRITE_SEGMENTS.has(segment));
|
|
7493
|
-
if (protectedSegment !== undefined) {
|
|
7494
|
-
throw new Error(`Task contract write path is protected: ${protectedSegment}`);
|
|
7495
|
-
}
|
|
7496
|
-
if (!isTaskStoragePath(segments)) {
|
|
7497
|
-
const protectedProjectAssetSegment = segments.find((segment) => FORBIDDEN_PROJECT_ASSET_SEGMENTS.has(segment));
|
|
7498
|
-
if (protectedProjectAssetSegment !== undefined) {
|
|
7499
|
-
throw new Error(`Task contract write path is protected: ${protectedProjectAssetSegment}`);
|
|
7500
|
-
}
|
|
7501
|
-
}
|
|
7502
|
-
const protectedFile = segments.find((segment) => FORBIDDEN_TASK_WRITE_FILES.has(segment));
|
|
7503
|
-
if (protectedFile !== undefined) {
|
|
7504
|
-
throw new Error(`Task contract write path is protected: ${protectedFile}`);
|
|
7505
|
-
}
|
|
7506
|
-
}
|
|
7507
|
-
async function resolveTaskWriteRealPath(path) {
|
|
7508
|
-
let currentPath = resolve3(path);
|
|
7509
|
-
const missingSegments = [];
|
|
7510
|
-
while (true) {
|
|
7511
|
-
try {
|
|
7512
|
-
await lstat(currentPath);
|
|
7513
|
-
return join12(await realpath(currentPath), ...missingSegments.reverse());
|
|
7514
|
-
} catch (error) {
|
|
7515
|
-
if (!isMissingPathError(error))
|
|
7516
|
-
throw error;
|
|
7517
|
-
const parentPath = dirname7(currentPath);
|
|
7518
|
-
if (parentPath === currentPath)
|
|
7519
|
-
return resolve3(path);
|
|
7520
|
-
missingSegments.push(basename(currentPath));
|
|
7521
|
-
currentPath = parentPath;
|
|
7522
|
-
}
|
|
7523
|
-
}
|
|
7524
|
-
}
|
|
7525
|
-
function isTaskStoragePath(segments) {
|
|
7526
|
-
return segments.some((segment, index) => segment === ".evodev" && (segments[index + 1] === "tasks" || segments[index + 1] === "state" && segments[index + 2] === "tasks"));
|
|
7527
|
-
}
|
|
7528
|
-
function sanitizeTextList(values) {
|
|
7529
|
-
return values.map((value) => sanitizeText2(value)).filter((value) => value.length > 0);
|
|
7530
|
-
}
|
|
7531
|
-
function sanitizeId2(value) {
|
|
7532
|
-
const sanitized = sanitizeText2(value).replace(/\[redacted\]/gi, "redacted").replace(/[^a-zA-Z0-9._-]/g, "-").replace(/-+/g, "-").replace(/^-+|-+$/g, "").slice(0, 80);
|
|
7533
|
-
return sanitized || "item";
|
|
7534
|
-
}
|
|
7535
|
-
function uniqueSanitizedIds2(values) {
|
|
7536
|
-
return Array.from(new Set(values.map((value) => sanitizeId2(value))));
|
|
7537
|
-
}
|
|
7538
|
-
function formatList(values) {
|
|
7539
|
-
return values.length === 0 ? "none" : values.join(", ");
|
|
7540
|
-
}
|
|
7541
|
-
function createTaskId(title) {
|
|
7542
|
-
const slug = sanitizeId2(title.toLowerCase()) || "task";
|
|
7543
|
-
return `task-${slug}`.slice(0, 80);
|
|
7544
|
-
}
|
|
7545
|
-
async function pathExists4(path) {
|
|
7546
|
-
try {
|
|
7547
|
-
await stat5(path);
|
|
7548
|
-
return true;
|
|
7549
|
-
} catch (error) {
|
|
7550
|
-
if (error instanceof Error && "code" in error && error.code === "ENOENT") {
|
|
7551
|
-
return false;
|
|
7552
|
-
}
|
|
7553
|
-
throw error;
|
|
7554
|
-
}
|
|
7555
|
-
}
|
|
7556
|
-
function isMissingPathError(error) {
|
|
7557
|
-
return error instanceof Error && "code" in error && (error.code === "ENOENT" || error.code === "ENOTDIR");
|
|
7558
|
-
}
|
|
7559
|
-
function isRecord7(value) {
|
|
7560
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
7561
|
-
}
|
|
7562
|
-
|
|
7563
7190
|
// packages/core/src/team/index.ts
|
|
7564
7191
|
import { spawn } from "node:child_process";
|
|
7565
|
-
import { appendFile as appendFile3, cp, mkdir as
|
|
7566
|
-
import { basename as basename2, dirname as dirname8, extname, isAbsolute as
|
|
7192
|
+
import { appendFile as appendFile3, cp, mkdir as mkdir7, readFile as readFile10, readdir as readdir9, stat as stat6, writeFile as writeFile6 } from "node:fs/promises";
|
|
7193
|
+
import { basename as basename2, dirname as dirname8, extname, isAbsolute as isAbsolute6, join as join13, relative as relative6, resolve as resolve3 } from "node:path";
|
|
7567
7194
|
|
|
7568
7195
|
// packages/core/src/team/prompts.ts
|
|
7569
7196
|
var TEAM_ROLE_STARTUP_PROMPT_TEMPLATE = [
|
|
@@ -7764,8 +7391,8 @@ function createTeamRunStore(homeDir) {
|
|
|
7764
7391
|
paths: paths2,
|
|
7765
7392
|
async createRunDirs(runId) {
|
|
7766
7393
|
await migrateLegacyRunDirIfNeeded(paths2, runId);
|
|
7767
|
-
await
|
|
7768
|
-
await
|
|
7394
|
+
await mkdir7(paths2.runDir(runId), { recursive: true });
|
|
7395
|
+
await mkdir7(paths2.agentsDir(runId), { recursive: true });
|
|
7769
7396
|
},
|
|
7770
7397
|
async writeRun(run) {
|
|
7771
7398
|
await this.createRunDirs(run.runId);
|
|
@@ -7776,7 +7403,7 @@ function createTeamRunStore(homeDir) {
|
|
|
7776
7403
|
return parseTeamRunRecord(JSON.parse(await readFile10(paths2.runPath(runId), "utf8")));
|
|
7777
7404
|
},
|
|
7778
7405
|
async writeLatestRunId(runId) {
|
|
7779
|
-
await
|
|
7406
|
+
await mkdir7(paths2.runsDir, { recursive: true });
|
|
7780
7407
|
await writeJson3(paths2.latestRunPath, { version: 1, runId });
|
|
7781
7408
|
},
|
|
7782
7409
|
async readLatestRunId() {
|
|
@@ -7810,7 +7437,7 @@ function createTeamRunStore(homeDir) {
|
|
|
7810
7437
|
async readAgents(runId) {
|
|
7811
7438
|
await migrateLegacyRunDirIfNeeded(paths2, runId);
|
|
7812
7439
|
try {
|
|
7813
|
-
const entries = await
|
|
7440
|
+
const entries = await readdir9(paths2.agentsDir(runId));
|
|
7814
7441
|
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)))));
|
|
7815
7442
|
return agents.sort((left, right) => left.roleId.localeCompare(right.roleId));
|
|
7816
7443
|
} catch {
|
|
@@ -7895,12 +7522,12 @@ function resolveTeamRunPaths(homeDir) {
|
|
|
7895
7522
|
}
|
|
7896
7523
|
async function migrateLegacyRunDirIfNeeded(paths2, runId) {
|
|
7897
7524
|
const nextDir = paths2.runDir(runId);
|
|
7898
|
-
if (await
|
|
7525
|
+
if (await pathExists4(nextDir))
|
|
7899
7526
|
return;
|
|
7900
7527
|
const legacyDir = paths2.legacyRunDir(runId);
|
|
7901
|
-
if (!await
|
|
7528
|
+
if (!await pathExists4(legacyDir))
|
|
7902
7529
|
return;
|
|
7903
|
-
await
|
|
7530
|
+
await mkdir7(dirname8(nextDir), { recursive: true });
|
|
7904
7531
|
await cp(legacyDir, nextDir, { recursive: true, errorOnExist: false, force: false });
|
|
7905
7532
|
}
|
|
7906
7533
|
async function writeTeamStatusSnapshot(input) {
|
|
@@ -8430,7 +8057,7 @@ async function listTeamRuns(input = {}) {
|
|
|
8430
8057
|
const entries = new Set;
|
|
8431
8058
|
for (const runsDir of [store.paths.runsDir, store.paths.legacyRunsDir]) {
|
|
8432
8059
|
try {
|
|
8433
|
-
for (const entry of await
|
|
8060
|
+
for (const entry of await readdir9(runsDir))
|
|
8434
8061
|
entries.add(entry);
|
|
8435
8062
|
} catch (error) {
|
|
8436
8063
|
if (!isNotFoundError3(error))
|
|
@@ -8763,7 +8390,7 @@ async function listTeamAgents(input = {}) {
|
|
|
8763
8390
|
}
|
|
8764
8391
|
async function resolveTeamOverlay(input) {
|
|
8765
8392
|
const repoTeamPath = join13(input.repoRoot, ".evodev", "team", "team.md");
|
|
8766
|
-
if (await
|
|
8393
|
+
if (await pathExists4(repoTeamPath)) {
|
|
8767
8394
|
return {
|
|
8768
8395
|
source: "repo",
|
|
8769
8396
|
teamPath: repoTeamPath,
|
|
@@ -8771,7 +8398,7 @@ async function resolveTeamOverlay(input) {
|
|
|
8771
8398
|
};
|
|
8772
8399
|
}
|
|
8773
8400
|
const globalTeamPath = resolveGlobalTeamMarkdownPath(input.homeDir);
|
|
8774
|
-
if (await
|
|
8401
|
+
if (await pathExists4(globalTeamPath)) {
|
|
8775
8402
|
return {
|
|
8776
8403
|
source: "global",
|
|
8777
8404
|
teamPath: globalTeamPath,
|
|
@@ -8830,12 +8457,12 @@ function resolveTeamAgentReference(input) {
|
|
|
8830
8457
|
if (reference.includes(":")) {
|
|
8831
8458
|
throw new Error(`Unsupported team agent reference for ${input.roleId}: ${reference}`);
|
|
8832
8459
|
}
|
|
8833
|
-
if (
|
|
8460
|
+
if (isAbsolute6(reference)) {
|
|
8834
8461
|
throw new Error(`Team agent path for ${input.roleId} must be repo-relative.`);
|
|
8835
8462
|
}
|
|
8836
|
-
const sourcePath =
|
|
8463
|
+
const sourcePath = resolve3(input.repoRoot, reference);
|
|
8837
8464
|
const relativePath = relative6(input.repoRoot, sourcePath);
|
|
8838
|
-
if (relativePath === "" || relativePath.startsWith("..") ||
|
|
8465
|
+
if (relativePath === "" || relativePath.startsWith("..") || isAbsolute6(relativePath) || extname(sourcePath) !== ".md") {
|
|
8839
8466
|
throw new Error(`Team agent path for ${input.roleId} must be a repo-local Markdown file.`);
|
|
8840
8467
|
}
|
|
8841
8468
|
return {
|
|
@@ -8860,7 +8487,7 @@ async function readTeamAgentDefinition(input) {
|
|
|
8860
8487
|
const reference = resolveTeamAgentReference(input);
|
|
8861
8488
|
const markdown = await readTeamAgentMarkdown(reference);
|
|
8862
8489
|
const parsed = parseMarkdownWithFrontmatter(markdown);
|
|
8863
|
-
const evodev =
|
|
8490
|
+
const evodev = isRecord7(parsed.frontmatter.evodev) ? parsed.frontmatter.evodev : {};
|
|
8864
8491
|
return {
|
|
8865
8492
|
roleId: input.roleId,
|
|
8866
8493
|
name: optionalString5(parsed.frontmatter.name) ?? defaultRoleName(input.roleId),
|
|
@@ -8977,7 +8604,7 @@ async function resolveTeamRole(input) {
|
|
|
8977
8604
|
};
|
|
8978
8605
|
}
|
|
8979
8606
|
function parseTeamRoleDefinition(value, defaults) {
|
|
8980
|
-
const input =
|
|
8607
|
+
const input = isRecord7(value) ? value : {};
|
|
8981
8608
|
const roleId = optionalString5(input.roleId) ?? defaults.roleId;
|
|
8982
8609
|
assertSafeId(roleId, "roleId");
|
|
8983
8610
|
const runtime = parseRuntime(input.runtime, defaults.defaultRuntime);
|
|
@@ -9201,7 +8828,7 @@ class TmuxRuntimeAdapter {
|
|
|
9201
8828
|
|
|
9202
8829
|
class NodeTeamRuntimeCommandRunner {
|
|
9203
8830
|
async run(command, args, options = {}) {
|
|
9204
|
-
return new Promise((
|
|
8831
|
+
return new Promise((resolve4, reject) => {
|
|
9205
8832
|
const child = spawn(command, args, { stdio: ["pipe", "pipe", "pipe"] });
|
|
9206
8833
|
let stdout = "";
|
|
9207
8834
|
let stderr = "";
|
|
@@ -9215,7 +8842,7 @@ class NodeTeamRuntimeCommandRunner {
|
|
|
9215
8842
|
});
|
|
9216
8843
|
child.once("error", reject);
|
|
9217
8844
|
child.once("close", (code) => {
|
|
9218
|
-
|
|
8845
|
+
resolve4({ exitCode: code ?? 1, stdout, stderr });
|
|
9219
8846
|
});
|
|
9220
8847
|
child.stdin.end(options.input ?? "");
|
|
9221
8848
|
});
|
|
@@ -9506,7 +9133,7 @@ async function listDefaultTeamOverlayAssets(assetsRootDir) {
|
|
|
9506
9133
|
await assertReadableFile(teamPath, "Default team asset");
|
|
9507
9134
|
let entries;
|
|
9508
9135
|
try {
|
|
9509
|
-
entries = await
|
|
9136
|
+
entries = await readdir9(agentsDir, { withFileTypes: true });
|
|
9510
9137
|
} catch (error) {
|
|
9511
9138
|
throw new Error(`Cannot read default team agents directory ${agentsDir}: ${describeError2(error)}`);
|
|
9512
9139
|
}
|
|
@@ -9541,8 +9168,8 @@ async function writeTextFileIfMissing(path, content) {
|
|
|
9541
9168
|
throw new Error(`Cannot inspect ${path}: ${describeError2(error)}`);
|
|
9542
9169
|
}
|
|
9543
9170
|
}
|
|
9544
|
-
await
|
|
9545
|
-
await
|
|
9171
|
+
await mkdir7(dirname8(path), { recursive: true });
|
|
9172
|
+
await writeFile6(path, content.endsWith(`
|
|
9546
9173
|
`) ? content : `${content}
|
|
9547
9174
|
`, "utf8");
|
|
9548
9175
|
return true;
|
|
@@ -9550,7 +9177,7 @@ async function writeTextFileIfMissing(path, content) {
|
|
|
9550
9177
|
function parseTeamDefinitionAgents(value) {
|
|
9551
9178
|
if (value === undefined)
|
|
9552
9179
|
return {};
|
|
9553
|
-
if (!
|
|
9180
|
+
if (!isRecord7(value))
|
|
9554
9181
|
throw new Error("team.md agents must be a role-id map.");
|
|
9555
9182
|
const agents = {};
|
|
9556
9183
|
for (const [roleId, reference] of Object.entries(value)) {
|
|
@@ -9616,7 +9243,7 @@ function parseSimpleYaml(content) {
|
|
|
9616
9243
|
throw new Error(`Invalid YAML line: ${trimmed}`);
|
|
9617
9244
|
const key = trimmed.slice(0, separator).trim();
|
|
9618
9245
|
const rawValue = trimmed.slice(separator + 1).trim();
|
|
9619
|
-
if (!
|
|
9246
|
+
if (!isRecord7(parent))
|
|
9620
9247
|
throw new Error(`Invalid YAML parent for key ${key}.`);
|
|
9621
9248
|
if (rawValue === "") {
|
|
9622
9249
|
const next = findNextYamlContentLine(lines, index + 1);
|
|
@@ -9681,7 +9308,7 @@ function resolveGlobalTeamMarkdownPath(homeDir) {
|
|
|
9681
9308
|
return join13(resolveEvoDevPaths(homeDir).rootDir, "team", "team.md");
|
|
9682
9309
|
}
|
|
9683
9310
|
function parseRolePermissions(value, main) {
|
|
9684
|
-
const input =
|
|
9311
|
+
const input = isRecord7(value) ? value : {};
|
|
9685
9312
|
return {
|
|
9686
9313
|
writeMode: parseWriteMode(input.writeMode, "repo-write"),
|
|
9687
9314
|
canUseTeamsMcp: optionalBoolean2(input.canUseTeamsMcp) ?? true,
|
|
@@ -9690,14 +9317,14 @@ function parseRolePermissions(value, main) {
|
|
|
9690
9317
|
};
|
|
9691
9318
|
}
|
|
9692
9319
|
function parseRolePolicy(value, recordTranscript) {
|
|
9693
|
-
const input =
|
|
9320
|
+
const input = isRecord7(value) ? value : {};
|
|
9694
9321
|
return {
|
|
9695
9322
|
roleInstancePolicy: "single-per-role",
|
|
9696
9323
|
recordTranscript: optionalBoolean2(input.recordTranscript) ?? recordTranscript
|
|
9697
9324
|
};
|
|
9698
9325
|
}
|
|
9699
9326
|
function parseTeamRunRecord(value) {
|
|
9700
|
-
if (!
|
|
9327
|
+
if (!isRecord7(value) || value.version !== 1)
|
|
9701
9328
|
throw new Error("Invalid team run record.");
|
|
9702
9329
|
const run = value;
|
|
9703
9330
|
assertSafeId(run.runId, "runId");
|
|
@@ -9707,7 +9334,7 @@ function parseTeamRunRecord(value) {
|
|
|
9707
9334
|
return run;
|
|
9708
9335
|
}
|
|
9709
9336
|
function parseTeamAgentRecord(value) {
|
|
9710
|
-
if (!
|
|
9337
|
+
if (!isRecord7(value) || value.version !== 1)
|
|
9711
9338
|
throw new Error("Invalid team agent record.");
|
|
9712
9339
|
const agent = value;
|
|
9713
9340
|
assertSafeId(agent.agentId, "agentId");
|
|
@@ -9728,7 +9355,7 @@ function parseTeamAgentRecord(value) {
|
|
|
9728
9355
|
].includes(agent.status)) {
|
|
9729
9356
|
throw new Error("Invalid agent status.");
|
|
9730
9357
|
}
|
|
9731
|
-
const nativeSessionValue =
|
|
9358
|
+
const nativeSessionValue = isRecord7(value.nativeSession) ? value.nativeSession : {};
|
|
9732
9359
|
return {
|
|
9733
9360
|
...agent,
|
|
9734
9361
|
nativeSession: {
|
|
@@ -9738,7 +9365,7 @@ function parseTeamAgentRecord(value) {
|
|
|
9738
9365
|
};
|
|
9739
9366
|
}
|
|
9740
9367
|
function parseTeamMessageRecord(value) {
|
|
9741
|
-
if (!
|
|
9368
|
+
if (!isRecord7(value) || value.version !== 1)
|
|
9742
9369
|
throw new Error("Invalid team message record.");
|
|
9743
9370
|
const message = value;
|
|
9744
9371
|
assertSafeId(message.messageId, "messageId");
|
|
@@ -9767,7 +9394,7 @@ function parseTeamPendingMessageRecord(value) {
|
|
|
9767
9394
|
};
|
|
9768
9395
|
}
|
|
9769
9396
|
function parseTeamMessageListFile(value) {
|
|
9770
|
-
if (!
|
|
9397
|
+
if (!isRecord7(value) || value.version !== 1)
|
|
9771
9398
|
throw new Error("Invalid team message list.");
|
|
9772
9399
|
const messages = Array.isArray(value.messages) ? value.messages.map(parseTeamPendingMessageRecord) : [];
|
|
9773
9400
|
return {
|
|
@@ -9777,7 +9404,7 @@ function parseTeamMessageListFile(value) {
|
|
|
9777
9404
|
};
|
|
9778
9405
|
}
|
|
9779
9406
|
function parseTeamEventRecord(value) {
|
|
9780
|
-
if (!
|
|
9407
|
+
if (!isRecord7(value) || value.version !== 1)
|
|
9781
9408
|
throw new Error("Invalid team event record.");
|
|
9782
9409
|
const event = value;
|
|
9783
9410
|
assertSafeId(event.eventId, "eventId");
|
|
@@ -9949,7 +9576,7 @@ async function readTeamBindingConfig(path) {
|
|
|
9949
9576
|
}
|
|
9950
9577
|
}
|
|
9951
9578
|
function parseTeamBindingConfig(value) {
|
|
9952
|
-
if (!
|
|
9579
|
+
if (!isRecord7(value) || value.version !== 1)
|
|
9953
9580
|
throw new Error("Invalid team binding config.");
|
|
9954
9581
|
const updatedAt = optionalString5(value.updatedAt) ?? "unknown";
|
|
9955
9582
|
const rolesValue = Array.isArray(value.roles) ? value.roles : [];
|
|
@@ -9960,7 +9587,7 @@ function parseTeamBindingConfig(value) {
|
|
|
9960
9587
|
};
|
|
9961
9588
|
}
|
|
9962
9589
|
function parseTeamBindingRecord(value) {
|
|
9963
|
-
if (!
|
|
9590
|
+
if (!isRecord7(value) || value.version !== 1) {
|
|
9964
9591
|
throw new Error("Invalid team binding record.");
|
|
9965
9592
|
}
|
|
9966
9593
|
const roleId = optionalString5(value.roleId);
|
|
@@ -9994,7 +9621,7 @@ function mergeTeamRoleBindings(global, project) {
|
|
|
9994
9621
|
function isNotFoundError3(error) {
|
|
9995
9622
|
return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
|
|
9996
9623
|
}
|
|
9997
|
-
async function
|
|
9624
|
+
async function pathExists4(path) {
|
|
9998
9625
|
try {
|
|
9999
9626
|
await stat6(path);
|
|
10000
9627
|
return true;
|
|
@@ -10005,20 +9632,20 @@ async function pathExists5(path) {
|
|
|
10005
9632
|
}
|
|
10006
9633
|
}
|
|
10007
9634
|
async function writeJson3(path, value) {
|
|
10008
|
-
await
|
|
10009
|
-
await
|
|
9635
|
+
await mkdir7(dirname8(path), { recursive: true });
|
|
9636
|
+
await writeFile6(path, `${JSON.stringify(value, null, 2)}
|
|
10010
9637
|
`, "utf8");
|
|
10011
9638
|
}
|
|
10012
9639
|
async function appendJsonLine2(path, value) {
|
|
10013
|
-
await
|
|
9640
|
+
await mkdir7(dirname8(path), { recursive: true });
|
|
10014
9641
|
await appendFile3(path, `${JSON.stringify(value)}
|
|
10015
9642
|
`, "utf8");
|
|
10016
9643
|
}
|
|
10017
|
-
async function readJsonLines(path,
|
|
9644
|
+
async function readJsonLines(path, parse2) {
|
|
10018
9645
|
try {
|
|
10019
9646
|
const text2 = await readFile10(path, "utf8");
|
|
10020
9647
|
return text2.split(`
|
|
10021
|
-
`).filter((line) => line.trim() !== "").map((line) =>
|
|
9648
|
+
`).filter((line) => line.trim() !== "").map((line) => parse2(JSON.parse(line)));
|
|
10022
9649
|
} catch (error) {
|
|
10023
9650
|
if (isNotFoundError3(error))
|
|
10024
9651
|
return [];
|
|
@@ -10048,7 +9675,7 @@ function optionalNullableString(value) {
|
|
|
10048
9675
|
function optionalBoolean2(value) {
|
|
10049
9676
|
return typeof value === "boolean" ? value : undefined;
|
|
10050
9677
|
}
|
|
10051
|
-
function
|
|
9678
|
+
function isRecord7(value) {
|
|
10052
9679
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
10053
9680
|
}
|
|
10054
9681
|
function describeError2(error) {
|
|
@@ -10108,7 +9735,7 @@ var DEFAULT_EVENT_SETTINGS = {
|
|
|
10108
9735
|
WorktreeCreate: true,
|
|
10109
9736
|
WorktreeRemove: true
|
|
10110
9737
|
};
|
|
10111
|
-
var
|
|
9738
|
+
var SENSITIVE_TEXT_PATTERN3 = /https?:\/\/\S+|\b(secret|token|password|passwd|private|internal|api[_-]?key|apikey|credential|credentials|\.env)\b/i;
|
|
10112
9739
|
var SOURCE_LIKE_PATTERN = /\b(function|class|import|export|const|let|var)\b.*[{};]/s;
|
|
10113
9740
|
var TEAM_MESSAGE_DELIVERY_EVENTS = new Set([
|
|
10114
9741
|
"SessionStart",
|
|
@@ -10152,9 +9779,9 @@ function parseHookSettings(value) {
|
|
|
10152
9779
|
const defaults = createDefaultHookSettings();
|
|
10153
9780
|
if (value === undefined || value === null)
|
|
10154
9781
|
return defaults;
|
|
10155
|
-
if (!
|
|
9782
|
+
if (!isRecord8(value))
|
|
10156
9783
|
throw new Error("Invalid hooks settings; expected object.");
|
|
10157
|
-
const observability =
|
|
9784
|
+
const observability = isRecord8(value.observability) ? value.observability : undefined;
|
|
10158
9785
|
optionalBoolean3(observability?.metadataOnly, defaults.observability.metadataOnly, "hooks.observability.metadataOnly");
|
|
10159
9786
|
optionalBoolean3(observability?.rawPayloadStorage, defaults.observability.rawPayloadStorage, "hooks.observability.rawPayloadStorage");
|
|
10160
9787
|
return {
|
|
@@ -10288,8 +9915,7 @@ function resolveHookRuntimeSessionPaths(input) {
|
|
|
10288
9915
|
const sessionDir = join14(input.homeDir, ".evodev", "STATE", "hooks", "sessions", input.sessionKey);
|
|
10289
9916
|
return {
|
|
10290
9917
|
sessionDir,
|
|
10291
|
-
bindingPath: join14(sessionDir, "binding.json")
|
|
10292
|
-
contractPath: join14(sessionDir, "contract.json")
|
|
9918
|
+
bindingPath: join14(sessionDir, "binding.json")
|
|
10293
9919
|
};
|
|
10294
9920
|
}
|
|
10295
9921
|
async function handleHookRuntime(input) {
|
|
@@ -10306,6 +9932,7 @@ async function handleHookRuntime(input) {
|
|
|
10306
9932
|
};
|
|
10307
9933
|
}
|
|
10308
9934
|
const diagnostics = await recordTeamNativeSessionFromHook(input);
|
|
9935
|
+
const projectDiagnostics = await recordDiscoveredProjectFromHook(input);
|
|
10309
9936
|
let result;
|
|
10310
9937
|
if (input.event.type === "SessionStart")
|
|
10311
9938
|
result = await handleSessionStart(input);
|
|
@@ -10333,7 +9960,8 @@ async function handleHookRuntime(input) {
|
|
|
10333
9960
|
const messageDiagnostics = await deliverPendingTeamMessagesFromHook(input, result);
|
|
10334
9961
|
const scopedContextDiagnostics = await injectScopedKnowledgeContextFromHook(input, result);
|
|
10335
9962
|
const teamStateDiagnostics = await recordTeamAgentHookStateFromHook(input);
|
|
10336
|
-
|
|
9963
|
+
const completed = appendRuntimeDiagnostics(appendRuntimeDiagnostics(appendRuntimeDiagnostics(appendRuntimeDiagnostics(appendRuntimeDiagnostics(appendRuntimeDiagnostics(appendRuntimeDiagnostics(result, diagnostics), projectDiagnostics), sessionMemoryDiagnostics), evolutionDiagnostics), messageDiagnostics), scopedContextDiagnostics), teamStateDiagnostics);
|
|
9964
|
+
return appendDevelopmentHookDiagnostics(input, completed);
|
|
10337
9965
|
}
|
|
10338
9966
|
function formatHookRuntimeOutput(result) {
|
|
10339
9967
|
return result.output === null ? "" : `${JSON.stringify(result.output)}
|
|
@@ -10342,6 +9970,29 @@ function formatHookRuntimeOutput(result) {
|
|
|
10342
9970
|
function isHookEventEnabled(settings, target, eventType) {
|
|
10343
9971
|
return settings.enabled === true && settings.targets[target]?.enabled === true && settings.targets[target]?.events[eventType] === true;
|
|
10344
9972
|
}
|
|
9973
|
+
async function recordDiscoveredProjectFromHook(input) {
|
|
9974
|
+
if (input.event.type !== "SessionStart" && input.event.type !== "UserPromptSubmit" && input.event.type !== "CwdChanged") {
|
|
9975
|
+
return { stateWrites: [], warnings: [] };
|
|
9976
|
+
}
|
|
9977
|
+
const cwd = optionalPayloadString(input.rawPayload.cwd);
|
|
9978
|
+
if (cwd === null)
|
|
9979
|
+
return { stateWrites: [], warnings: [] };
|
|
9980
|
+
try {
|
|
9981
|
+
const result = await recordDiscoveredProject({
|
|
9982
|
+
homeDir: input.homeDir,
|
|
9983
|
+
cwd,
|
|
9984
|
+
target: input.target,
|
|
9985
|
+
sessionKey: resolveTraceSessionKey(input.rawPayload),
|
|
9986
|
+
now: input.receivedAt === undefined || input.receivedAt === "dry-run" ? undefined : input.receivedAt
|
|
9987
|
+
});
|
|
9988
|
+
return result === null ? { stateWrites: [], warnings: [] } : { stateWrites: [result.path], warnings: [] };
|
|
9989
|
+
} catch {
|
|
9990
|
+
return {
|
|
9991
|
+
stateWrites: [],
|
|
9992
|
+
warnings: ["Local project discovery could not be updated at this hook safe point."]
|
|
9993
|
+
};
|
|
9994
|
+
}
|
|
9995
|
+
}
|
|
10345
9996
|
async function recordTeamNativeSessionFromHook(input) {
|
|
10346
9997
|
const environment = input.environment ?? {};
|
|
10347
9998
|
const runId = optionalPayloadString(environment.EVODEV_TEAM_RUN_ID);
|
|
@@ -10580,41 +10231,27 @@ async function handleSessionStart(input) {
|
|
|
10580
10231
|
});
|
|
10581
10232
|
}
|
|
10582
10233
|
async function handleUserPromptSubmit(input) {
|
|
10583
|
-
const classification = classifyUserPrompt(input.rawPayload.prompt ?? input.rawPayload.userPrompt);
|
|
10584
10234
|
const sessionKey = resolveHookSessionKey(input.rawPayload);
|
|
10585
10235
|
const paths2 = resolveHookRuntimeSessionPaths({ homeDir: input.homeDir, sessionKey });
|
|
10586
|
-
const contract = routeTaskContract(createHookTaskContract(input, classification));
|
|
10587
10236
|
const previousBinding = await readSessionBinding(input.homeDir, input.rawPayload);
|
|
10588
10237
|
const teamRuntimeContext = previousBinding?.teamRuntimeContextDeliveredAt === undefined || previousBinding.teamRuntimeContextDeliveredAt === null ? await createTeamRuntimeContextForUserPrompt(input) : null;
|
|
10589
|
-
const shouldShowDiagnostics = input.teamRuntimeDisplayMode === "development";
|
|
10590
10238
|
const teamRuntimeContextDeliveredAt = teamRuntimeContext !== null ? input.receivedAt ?? new Date().toISOString() : previousBinding?.teamRuntimeContextDeliveredAt ?? null;
|
|
10591
10239
|
const binding = {
|
|
10592
10240
|
version: 1,
|
|
10593
10241
|
target: input.target,
|
|
10594
10242
|
sessionKey,
|
|
10595
|
-
taskId: contract.taskId,
|
|
10596
|
-
contractPath: paths2.contractPath,
|
|
10597
10243
|
cwd: optionalPayloadString(input.rawPayload.cwd),
|
|
10598
|
-
route: contract.route,
|
|
10599
10244
|
teamRuntimeContextDeliveredAt,
|
|
10600
10245
|
updatedAt: input.receivedAt ?? new Date().toISOString()
|
|
10601
10246
|
};
|
|
10602
|
-
await writeTaskContract(paths2.contractPath, contract, { overwrite: true });
|
|
10603
10247
|
await writeJsonFile2(paths2.bindingPath, binding);
|
|
10604
|
-
|
|
10605
|
-
classification,
|
|
10606
|
-
contract,
|
|
10607
|
-
contractPath: paths2.contractPath
|
|
10608
|
-
});
|
|
10609
|
-
let output = visibleContext === null || !shouldShowDiagnostics ? null : hookOutput(input.event.type, {
|
|
10610
|
-
additionalContext: visibleContext
|
|
10611
|
-
});
|
|
10248
|
+
let output = null;
|
|
10612
10249
|
if (teamRuntimeContext !== null) {
|
|
10613
10250
|
output = appendAdditionalContext(output, input.event.type, teamRuntimeContext);
|
|
10614
10251
|
}
|
|
10615
10252
|
return createRuntimeResult(input, output, {
|
|
10616
|
-
summary: "User prompt observed;
|
|
10617
|
-
stateWrites: [paths2.
|
|
10253
|
+
summary: "User prompt observed; session binding updated.",
|
|
10254
|
+
stateWrites: [paths2.bindingPath]
|
|
10618
10255
|
});
|
|
10619
10256
|
}
|
|
10620
10257
|
async function createTeamRuntimeContextForUserPrompt(input) {
|
|
@@ -10642,40 +10279,11 @@ async function handlePreToolUse(input) {
|
|
|
10642
10279
|
});
|
|
10643
10280
|
}
|
|
10644
10281
|
async function handlePostToolUse(input) {
|
|
10645
|
-
|
|
10646
|
-
const binding = await readSessionBinding(input.homeDir, input.rawPayload);
|
|
10647
|
-
if (contract === null || binding?.contractPath === null || binding?.contractPath === undefined) {
|
|
10648
|
-
return handleAdditionalContext(input, input.event.type);
|
|
10649
|
-
}
|
|
10650
|
-
const status = optionalPayloadNumber(input.rawPayload.exit_code ?? input.rawPayload.exitCode);
|
|
10651
|
-
const nextContract = {
|
|
10652
|
-
...contract,
|
|
10653
|
-
evidence: {
|
|
10654
|
-
metadataOnly: true,
|
|
10655
|
-
items: [
|
|
10656
|
-
...contract.evidence.items,
|
|
10657
|
-
{
|
|
10658
|
-
type: "command-result",
|
|
10659
|
-
id: input.event.eventId,
|
|
10660
|
-
status: status === 0 ? "pass" : status === null ? "unknown" : "fail",
|
|
10661
|
-
summary: input.event.payload.summary,
|
|
10662
|
-
rawOutputStored: false,
|
|
10663
|
-
sourceContentStored: false
|
|
10664
|
-
}
|
|
10665
|
-
]
|
|
10666
|
-
}
|
|
10667
|
-
};
|
|
10668
|
-
await writeTaskContract(binding.contractPath, nextContract, { overwrite: true });
|
|
10669
|
-
return createRuntimeResult(input, null, {
|
|
10670
|
-
summary: "Post-tool metadata evidence recorded.",
|
|
10671
|
-
stateWrites: [binding.contractPath]
|
|
10672
|
-
});
|
|
10282
|
+
return handleAdditionalContext(input, input.event.type);
|
|
10673
10283
|
}
|
|
10674
10284
|
async function handleCompletionObservation(input) {
|
|
10675
|
-
const contract = await readActiveContract(input.homeDir, input.rawPayload);
|
|
10676
|
-
const suffix = contract === null ? "without an active Task Contract" : `for Task Contract ${contract.taskId}`;
|
|
10677
10285
|
return createRuntimeResult(input, null, {
|
|
10678
|
-
summary: `${input.event.type} observed
|
|
10286
|
+
summary: `${input.event.type} observed as metadata-only hook context.`
|
|
10679
10287
|
});
|
|
10680
10288
|
}
|
|
10681
10289
|
async function handlePreCompact(input) {
|
|
@@ -10726,7 +10334,7 @@ function hookOutput(eventName, output) {
|
|
|
10726
10334
|
function appendAdditionalContext(output, eventName, context) {
|
|
10727
10335
|
if (output === null)
|
|
10728
10336
|
return hookOutput(eventName, { additionalContext: context });
|
|
10729
|
-
const hookSpecificOutput =
|
|
10337
|
+
const hookSpecificOutput = isRecord8(output.hookSpecificOutput) ? output.hookSpecificOutput : {};
|
|
10730
10338
|
const previous = typeof hookSpecificOutput.additionalContext === "string" ? hookSpecificOutput.additionalContext : "";
|
|
10731
10339
|
return {
|
|
10732
10340
|
...output,
|
|
@@ -10739,6 +10347,31 @@ ${context}`
|
|
|
10739
10347
|
}
|
|
10740
10348
|
};
|
|
10741
10349
|
}
|
|
10350
|
+
function appendDevelopmentHookDiagnostics(input, result) {
|
|
10351
|
+
if (input.teamRuntimeDisplayMode !== "development" || !canDeliverTeamMessagesFromHook(input.target, input.event.type)) {
|
|
10352
|
+
return result;
|
|
10353
|
+
}
|
|
10354
|
+
return {
|
|
10355
|
+
...result,
|
|
10356
|
+
output: appendAdditionalContext(result.output, input.event.type, formatDevelopmentHookDiagnostics(input))
|
|
10357
|
+
};
|
|
10358
|
+
}
|
|
10359
|
+
function formatDevelopmentHookDiagnostics(input) {
|
|
10360
|
+
const metadata = Object.entries(input.event.payload.metadata);
|
|
10361
|
+
const inputFields = Object.keys(input.rawPayload).map((field) => field.replace(/[^A-Za-z0-9._-]/g, "-").slice(0, 80)).filter((field) => field.length > 0).slice(0, 30);
|
|
10362
|
+
return [
|
|
10363
|
+
"EvoDev hook development diagnostics",
|
|
10364
|
+
`Target: ${input.target}`,
|
|
10365
|
+
`Event: ${input.event.type}`,
|
|
10366
|
+
`Summary: ${input.event.payload.summary}`,
|
|
10367
|
+
`Input fields: ${inputFields.join(", ") || "none"}`,
|
|
10368
|
+
"Normalized metadata:",
|
|
10369
|
+
...metadata.length === 0 ? ["- none"] : metadata.map(([key, value]) => `- ${key}: ${formatMetadataValue(value)}`),
|
|
10370
|
+
`Redactions: ${input.event.payload.redactions.join(", ") || "none"}`,
|
|
10371
|
+
"Raw payload included: false"
|
|
10372
|
+
].join(`
|
|
10373
|
+
`);
|
|
10374
|
+
}
|
|
10742
10375
|
function formatTeamInboxContext(messages) {
|
|
10743
10376
|
const blocks = messages.map((message) => [
|
|
10744
10377
|
"[EvoDev team message]",
|
|
@@ -10762,101 +10395,6 @@ function truncateTeamMessageBody(value) {
|
|
|
10762
10395
|
return value;
|
|
10763
10396
|
return `${value.slice(0, 4000)}...[truncated:${value.length - 4000}]`;
|
|
10764
10397
|
}
|
|
10765
|
-
function createUserPromptVisibleContext(input) {
|
|
10766
|
-
if (!input.classification.needsClarification)
|
|
10767
|
-
return null;
|
|
10768
|
-
return [
|
|
10769
|
-
"EvoDev advisory: clarification may be needed before broad changes.",
|
|
10770
|
-
`Task Contract: ${input.contractPath}`,
|
|
10771
|
-
`Suggested mode: ${input.contract.route.mode ?? "unknown"}`,
|
|
10772
|
-
`Suggested workflow: ${input.contract.route.workflowId ?? "none"}`,
|
|
10773
|
-
`Reason: ${input.contract.route.rationale}`
|
|
10774
|
-
].join(" ");
|
|
10775
|
-
}
|
|
10776
|
-
function createHookTaskContract(input, classification) {
|
|
10777
|
-
const sessionKey = resolveHookSessionKey(input.rawPayload);
|
|
10778
|
-
const targetName = formatHookTargetName(input.target);
|
|
10779
|
-
const contract = createTaskContract({
|
|
10780
|
-
title: `${targetName} hook task ${sessionKey}`,
|
|
10781
|
-
summary: `${targetName} prompt classified as ${classification.kind}; raw prompt is not stored by EvoDev.`,
|
|
10782
|
-
projectId: null
|
|
10783
|
-
});
|
|
10784
|
-
return {
|
|
10785
|
-
...contract,
|
|
10786
|
-
currentState: {
|
|
10787
|
-
summary: `UserPromptSubmit received through ${targetName} hooks; raw prompt omitted from Task Contract.`,
|
|
10788
|
-
evidenceRefs: []
|
|
10789
|
-
},
|
|
10790
|
-
targetState: {
|
|
10791
|
-
summary: `Complete the ${classification.kind} task through EvoDev-controlled workflow.`,
|
|
10792
|
-
nonGoals: ["Do not store raw prompts, transcripts, source content, secrets, or raw output."],
|
|
10793
|
-
constraints: [
|
|
10794
|
-
`prompt-kind:${classification.kind}`,
|
|
10795
|
-
...classification.riskTerms.map((term) => `risk:${term}`)
|
|
10796
|
-
]
|
|
10797
|
-
},
|
|
10798
|
-
scope: {
|
|
10799
|
-
...contract.scope,
|
|
10800
|
-
requiresUserConfirmation: classification.riskTerms
|
|
10801
|
-
},
|
|
10802
|
-
context: {
|
|
10803
|
-
...contract.context,
|
|
10804
|
-
assumptions: [
|
|
10805
|
-
`hook-session:${sessionKey}`,
|
|
10806
|
-
`cwd:${optionalPayloadString(input.rawPayload.cwd) ?? "unknown"}`
|
|
10807
|
-
],
|
|
10808
|
-
openQuestions: classification.needsClarification ? ["User request may need clarification before broad changes."] : []
|
|
10809
|
-
}
|
|
10810
|
-
};
|
|
10811
|
-
}
|
|
10812
|
-
function formatHookTargetName(target) {
|
|
10813
|
-
return target === "codex" ? "Codex" : "Claude";
|
|
10814
|
-
}
|
|
10815
|
-
function classifyUserPrompt(value) {
|
|
10816
|
-
const text2 = typeof value === "string" ? value.toLowerCase() : "";
|
|
10817
|
-
const riskTerms = [
|
|
10818
|
-
"security",
|
|
10819
|
-
"release",
|
|
10820
|
-
"publish",
|
|
10821
|
-
"hook",
|
|
10822
|
-
"memory",
|
|
10823
|
-
"learning",
|
|
10824
|
-
"secret",
|
|
10825
|
-
"privacy"
|
|
10826
|
-
].filter((term) => text2.includes(term));
|
|
10827
|
-
let kind = "feature";
|
|
10828
|
-
if (/\bbug|fix|error|failed|failure\b/.test(text2))
|
|
10829
|
-
kind = "bugfix";
|
|
10830
|
-
if (/\brefactor|migration|migrate\b/.test(text2))
|
|
10831
|
-
kind = "refactor";
|
|
10832
|
-
if (/\breview|audit\b/.test(text2))
|
|
10833
|
-
kind = "review";
|
|
10834
|
-
if (/\btest|coverage\b/.test(text2))
|
|
10835
|
-
kind = "test";
|
|
10836
|
-
if (/\bdoc|readme|guide\b/.test(text2))
|
|
10837
|
-
kind = "docs";
|
|
10838
|
-
if (riskTerms.includes("security") || riskTerms.includes("privacy"))
|
|
10839
|
-
kind = "security";
|
|
10840
|
-
if (riskTerms.includes("release") || riskTerms.includes("publish"))
|
|
10841
|
-
kind = "release";
|
|
10842
|
-
return {
|
|
10843
|
-
kind,
|
|
10844
|
-
riskTerms,
|
|
10845
|
-
needsClarification: text2.trim().length < 12 || /\bmaybe|unclear|not sure\b/.test(text2)
|
|
10846
|
-
};
|
|
10847
|
-
}
|
|
10848
|
-
async function readActiveContract(homeDir, payload) {
|
|
10849
|
-
const binding = await readSessionBinding(homeDir, payload);
|
|
10850
|
-
if (binding?.contractPath === null || binding?.contractPath === undefined)
|
|
10851
|
-
return null;
|
|
10852
|
-
try {
|
|
10853
|
-
return JSON.parse(await readFile11(binding.contractPath, "utf8"));
|
|
10854
|
-
} catch (error) {
|
|
10855
|
-
if (isNotFoundError4(error))
|
|
10856
|
-
return null;
|
|
10857
|
-
throw error;
|
|
10858
|
-
}
|
|
10859
|
-
}
|
|
10860
10398
|
async function readSessionBinding(homeDir, payload) {
|
|
10861
10399
|
const sessionKey = resolveHookSessionKey(payload);
|
|
10862
10400
|
const paths2 = resolveHookRuntimeSessionPaths({ homeDir, sessionKey });
|
|
@@ -10875,8 +10413,8 @@ function resolveHookSessionKey(payload) {
|
|
|
10875
10413
|
return `session-${sha256Short(source)}`;
|
|
10876
10414
|
}
|
|
10877
10415
|
async function writeJsonFile2(path, value) {
|
|
10878
|
-
await
|
|
10879
|
-
await
|
|
10416
|
+
await mkdir8(dirname9(path), { recursive: true });
|
|
10417
|
+
await writeFile7(path, `${JSON.stringify(value, null, 2)}
|
|
10880
10418
|
`, "utf8");
|
|
10881
10419
|
}
|
|
10882
10420
|
function optionalPayloadString(value) {
|
|
@@ -10885,9 +10423,6 @@ function optionalPayloadString(value) {
|
|
|
10885
10423
|
function safeDiagnosticId(value) {
|
|
10886
10424
|
return value.replace(/[^A-Za-z0-9._-]/g, "-").slice(0, 120) || "unknown";
|
|
10887
10425
|
}
|
|
10888
|
-
function optionalPayloadNumber(value) {
|
|
10889
|
-
return typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
10890
|
-
}
|
|
10891
10426
|
function isNotFoundError4(error) {
|
|
10892
10427
|
return error instanceof Error && "code" in error && error.code === "ENOENT";
|
|
10893
10428
|
}
|
|
@@ -10901,9 +10436,9 @@ function normalizeHookEventType(type, warnings) {
|
|
|
10901
10436
|
throw new Error(`Unsupported hook event type: ${type}`);
|
|
10902
10437
|
}
|
|
10903
10438
|
function parseHookTargetSettings(value, defaults, target) {
|
|
10904
|
-
const targets =
|
|
10905
|
-
const targetSettings =
|
|
10906
|
-
const events =
|
|
10439
|
+
const targets = isRecord8(value) ? value : {};
|
|
10440
|
+
const targetSettings = isRecord8(targets[target]) ? targets[target] : {};
|
|
10441
|
+
const events = isRecord8(targetSettings.events) ? targetSettings.events : {};
|
|
10907
10442
|
const parsedEvents = { ...defaults.events };
|
|
10908
10443
|
for (const eventType of CANONICAL_HOOK_EVENT_TYPES) {
|
|
10909
10444
|
parsedEvents[eventType] = optionalBoolean3(events[eventType], defaults.events[eventType], `hooks.targets.${target}.events.${eventType}`);
|
|
@@ -10915,7 +10450,7 @@ function parseHookTargetSettings(value, defaults, target) {
|
|
|
10915
10450
|
}
|
|
10916
10451
|
function extractMetadata(type, payload, redactions) {
|
|
10917
10452
|
const metadata = {};
|
|
10918
|
-
const toolInput =
|
|
10453
|
+
const toolInput = isRecord8(payload.tool_input) ? payload.tool_input : isRecord8(payload.toolInput) ? payload.toolInput : {};
|
|
10919
10454
|
const toolName = optionalSanitizedString(payload.tool_name ?? payload.toolName, redactions);
|
|
10920
10455
|
if (toolName !== null)
|
|
10921
10456
|
metadata.toolName = toolName;
|
|
@@ -10962,7 +10497,7 @@ function summarizeEvent(type, metadata) {
|
|
|
10962
10497
|
function optionalSanitizedString(value, redactions, options = {}) {
|
|
10963
10498
|
if (typeof value !== "string" || value.length === 0)
|
|
10964
10499
|
return null;
|
|
10965
|
-
if (
|
|
10500
|
+
if (SENSITIVE_TEXT_PATTERN3.test(value) || SOURCE_LIKE_PATTERN.test(value)) {
|
|
10966
10501
|
redactions.push(options.classifyOnly ? "sensitive-command" : "sensitive-text");
|
|
10967
10502
|
return options.classifyOnly ? value : "[redacted]";
|
|
10968
10503
|
}
|
|
@@ -10993,7 +10528,7 @@ function optionalNumber(value) {
|
|
|
10993
10528
|
function formatMetadataValue(value) {
|
|
10994
10529
|
return Array.isArray(value) ? value.join(",") : String(value);
|
|
10995
10530
|
}
|
|
10996
|
-
function
|
|
10531
|
+
function isRecord8(value) {
|
|
10997
10532
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
10998
10533
|
}
|
|
10999
10534
|
|
|
@@ -11295,20 +10830,20 @@ function expectNonNegativeInteger(value, path) {
|
|
|
11295
10830
|
return value;
|
|
11296
10831
|
}
|
|
11297
10832
|
// packages/core/src/config/store.ts
|
|
11298
|
-
import { mkdir as
|
|
10833
|
+
import { mkdir as mkdir9, readFile as readFile13, writeFile as writeFile8 } from "node:fs/promises";
|
|
11299
10834
|
import { dirname as dirname10 } from "node:path";
|
|
11300
10835
|
function createCoreConfigStore(homeDir) {
|
|
11301
10836
|
const paths2 = resolveEvoDevPaths(homeDir);
|
|
11302
10837
|
return {
|
|
11303
10838
|
paths: paths2,
|
|
11304
10839
|
async ensureBaseDirs() {
|
|
11305
|
-
await
|
|
11306
|
-
await
|
|
11307
|
-
await
|
|
11308
|
-
await
|
|
11309
|
-
await
|
|
11310
|
-
await
|
|
11311
|
-
await
|
|
10840
|
+
await mkdir9(paths2.stateDir, { recursive: true });
|
|
10841
|
+
await mkdir9(paths2.logsDir, { recursive: true });
|
|
10842
|
+
await mkdir9(paths2.knowledgeDir, { recursive: true });
|
|
10843
|
+
await mkdir9(paths2.evosCasesDir, { recursive: true });
|
|
10844
|
+
await mkdir9(paths2.roleAgentsDir, { recursive: true });
|
|
10845
|
+
await mkdir9(paths2.teamsDir, { recursive: true });
|
|
10846
|
+
await mkdir9(paths2.runsDir, { recursive: true });
|
|
11312
10847
|
},
|
|
11313
10848
|
async ensureKnowledgeBase() {
|
|
11314
10849
|
await ensureKnowledgeBaseFiles(paths2);
|
|
@@ -11356,8 +10891,8 @@ async function initializeCoreConfig(homeDir) {
|
|
|
11356
10891
|
return store;
|
|
11357
10892
|
}
|
|
11358
10893
|
async function ensureKnowledgeBaseFiles(paths2) {
|
|
11359
|
-
await
|
|
11360
|
-
await
|
|
10894
|
+
await mkdir9(paths2.knowledgeDir, { recursive: true });
|
|
10895
|
+
await mkdir9(paths2.evosCasesDir, { recursive: true });
|
|
11361
10896
|
await ensureOkfKnowledgeBase(paths2.homeDir);
|
|
11362
10897
|
await writeTextIfMissing2(`${paths2.knowledgeDir}/README.md`, [
|
|
11363
10898
|
"# EvoDev Knowledge",
|
|
@@ -11426,7 +10961,7 @@ async function ensureKnowledgeBaseFiles(paths2) {
|
|
|
11426
10961
|
teams: []
|
|
11427
10962
|
});
|
|
11428
10963
|
}
|
|
11429
|
-
async function readJsonFile2(filePath,
|
|
10964
|
+
async function readJsonFile2(filePath, parse2) {
|
|
11430
10965
|
let raw;
|
|
11431
10966
|
try {
|
|
11432
10967
|
raw = await readFile13(filePath, "utf8");
|
|
@@ -11440,7 +10975,7 @@ async function readJsonFile2(filePath, parse) {
|
|
|
11440
10975
|
throw new EvoDevConfigError(`Invalid JSON (${describeFileError2(error)})`, filePath);
|
|
11441
10976
|
}
|
|
11442
10977
|
try {
|
|
11443
|
-
return
|
|
10978
|
+
return parse2(json);
|
|
11444
10979
|
} catch (error) {
|
|
11445
10980
|
if (error instanceof EvoDevConfigError) {
|
|
11446
10981
|
throw new EvoDevConfigError(error.message, filePath);
|
|
@@ -11448,9 +10983,9 @@ async function readJsonFile2(filePath, parse) {
|
|
|
11448
10983
|
throw error;
|
|
11449
10984
|
}
|
|
11450
10985
|
}
|
|
11451
|
-
async function readJsonFileOrDefault(filePath,
|
|
10986
|
+
async function readJsonFileOrDefault(filePath, parse2, fallback) {
|
|
11452
10987
|
try {
|
|
11453
|
-
return await readJsonFile2(filePath,
|
|
10988
|
+
return await readJsonFile2(filePath, parse2);
|
|
11454
10989
|
} catch (error) {
|
|
11455
10990
|
if (error instanceof EvoDevConfigError && error.message.includes("ENOENT")) {
|
|
11456
10991
|
return fallback;
|
|
@@ -11486,7 +11021,7 @@ async function writeIndexIfMissingOrMigrate(filePath, kind, defaults) {
|
|
|
11486
11021
|
} catch (error) {
|
|
11487
11022
|
throw new EvoDevConfigError(`Invalid bootstrap index JSON (${describeFileError2(error)})`, filePath);
|
|
11488
11023
|
}
|
|
11489
|
-
if (!
|
|
11024
|
+
if (!isRecord9(existing) || existing.kind !== kind)
|
|
11490
11025
|
return;
|
|
11491
11026
|
const migrated = { ...defaults, ...existing };
|
|
11492
11027
|
if (Object.keys(defaults).every((key) => (key in existing)))
|
|
@@ -11498,16 +11033,16 @@ async function writeTextIfMissing2(filePath, value) {
|
|
|
11498
11033
|
await readFile13(filePath, "utf8");
|
|
11499
11034
|
} catch (error) {
|
|
11500
11035
|
if (isNodeError2(error) && error.code === "ENOENT") {
|
|
11501
|
-
await
|
|
11502
|
-
await
|
|
11036
|
+
await mkdir9(dirname10(filePath), { recursive: true });
|
|
11037
|
+
await writeFile8(filePath, value, "utf8");
|
|
11503
11038
|
return;
|
|
11504
11039
|
}
|
|
11505
11040
|
throw new EvoDevConfigError(`Cannot inspect config file (${describeFileError2(error)})`, filePath);
|
|
11506
11041
|
}
|
|
11507
11042
|
}
|
|
11508
11043
|
async function writeJsonFile3(filePath, value) {
|
|
11509
|
-
await
|
|
11510
|
-
await
|
|
11044
|
+
await mkdir9(dirname10(filePath), { recursive: true });
|
|
11045
|
+
await writeFile8(filePath, `${JSON.stringify(value, null, 2)}
|
|
11511
11046
|
`, "utf8");
|
|
11512
11047
|
}
|
|
11513
11048
|
function describeFileError2(error) {
|
|
@@ -11519,12 +11054,12 @@ function describeFileError2(error) {
|
|
|
11519
11054
|
function isNodeError2(error) {
|
|
11520
11055
|
return error instanceof Error && "code" in error;
|
|
11521
11056
|
}
|
|
11522
|
-
function
|
|
11057
|
+
function isRecord9(value) {
|
|
11523
11058
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
11524
11059
|
}
|
|
11525
11060
|
// packages/core/src/daemon/index.ts
|
|
11526
11061
|
import { randomBytes } from "node:crypto";
|
|
11527
|
-
import { mkdir as
|
|
11062
|
+
import { mkdir as mkdir12, readFile as readFile16, readdir as readdir12, rm as rm4, stat as stat9, writeFile as writeFile11 } from "node:fs/promises";
|
|
11528
11063
|
import { createServer } from "node:http";
|
|
11529
11064
|
import { dirname as dirname13, join as join19 } from "node:path";
|
|
11530
11065
|
|
|
@@ -11676,7 +11211,7 @@ __export(exports_processor, {
|
|
|
11676
11211
|
});
|
|
11677
11212
|
|
|
11678
11213
|
// packages/core/src/evolution/evidence/analysis.ts
|
|
11679
|
-
import { readFile as readFile14, readdir as
|
|
11214
|
+
import { readFile as readFile14, readdir as readdir10 } from "node:fs/promises";
|
|
11680
11215
|
import { basename as basename3, join as join15 } from "node:path";
|
|
11681
11216
|
async function analyzeEvolutionRun(input) {
|
|
11682
11217
|
const projectKey = resolveEvolutionProjectKey(input);
|
|
@@ -11689,8 +11224,8 @@ async function analyzeEvolutionRun(input) {
|
|
|
11689
11224
|
const logsDir = resolveEvoDevPaths(input.homeDir).logsDir;
|
|
11690
11225
|
const projectRunAgentsDir = join15(logsDir, "teams", projectKey, runId, "agents");
|
|
11691
11226
|
const legacyProjectRunAgentsDir = join15(logsDir, projectKey, runId, "agents");
|
|
11692
|
-
const agentDirs = await pathExists(projectRunAgentsDir) ? await
|
|
11693
|
-
const legacyAgentDirs = await pathExists(legacyProjectRunAgentsDir) ? await
|
|
11227
|
+
const agentDirs = await pathExists(projectRunAgentsDir) ? await readdir10(projectRunAgentsDir, { withFileTypes: true }) : [];
|
|
11228
|
+
const legacyAgentDirs = await pathExists(legacyProjectRunAgentsDir) ? await readdir10(legacyProjectRunAgentsDir, { withFileTypes: true }) : [];
|
|
11694
11229
|
if (agentDirs.length === 0 && legacyAgentDirs.length === 0) {
|
|
11695
11230
|
warnings.push(`No agent execution event directory found: ${displayPath(input.homeDir, projectRunAgentsDir)}`);
|
|
11696
11231
|
} else {
|
|
@@ -12376,7 +11911,7 @@ function createProvenance(evidenceWindow, createdAt, sourceRefs) {
|
|
|
12376
11911
|
};
|
|
12377
11912
|
}
|
|
12378
11913
|
// packages/core/src/evolution/processor/process.ts
|
|
12379
|
-
import { mkdir as
|
|
11914
|
+
import { mkdir as mkdir10, rm as rm3, stat as stat7, writeFile as writeFile9 } from "node:fs/promises";
|
|
12380
11915
|
import { dirname as dirname11, join as join17 } from "node:path";
|
|
12381
11916
|
var PROCESS_LOCK_STALE_MS2 = 5 * 60 * 1000;
|
|
12382
11917
|
async function processEvolutionTriggers(input) {
|
|
@@ -12537,9 +12072,9 @@ async function processEvolutionTriggers(input) {
|
|
|
12537
12072
|
async function acquireEvolutionProcessLock(homeDir, now) {
|
|
12538
12073
|
const paths2 = resolveEvoDevPaths(homeDir);
|
|
12539
12074
|
const lockPath = join17(paths2.stateDir, "evolution", ".process.lock");
|
|
12540
|
-
await
|
|
12075
|
+
await mkdir10(dirname11(lockPath), { recursive: true });
|
|
12541
12076
|
try {
|
|
12542
|
-
await
|
|
12077
|
+
await writeFile9(lockPath, `${JSON.stringify({
|
|
12543
12078
|
schemaVersion: 1,
|
|
12544
12079
|
kind: "evolution-process-lock",
|
|
12545
12080
|
createdAt: now,
|
|
@@ -12562,7 +12097,7 @@ async function releaseEvolutionProcessLock(lock) {
|
|
|
12562
12097
|
await rm3(lock.path, { force: true });
|
|
12563
12098
|
}
|
|
12564
12099
|
// packages/core/src/observability/index.ts
|
|
12565
|
-
import { mkdir as
|
|
12100
|
+
import { mkdir as mkdir11, readFile as readFile15, readdir as readdir11, stat as stat8, writeFile as writeFile10 } from "node:fs/promises";
|
|
12566
12101
|
import { dirname as dirname12, join as join18 } from "node:path";
|
|
12567
12102
|
var EVENT_TYPE_TO_DIR = {
|
|
12568
12103
|
"verification.completed": "verification",
|
|
@@ -12570,7 +12105,7 @@ var EVENT_TYPE_TO_DIR = {
|
|
|
12570
12105
|
"test-run.completed": "tests",
|
|
12571
12106
|
"review.finding": "reviews"
|
|
12572
12107
|
};
|
|
12573
|
-
var
|
|
12108
|
+
var FORBIDDEN_RAW_KEYS2 = new Set([
|
|
12574
12109
|
"commandoutput",
|
|
12575
12110
|
"memorybodies",
|
|
12576
12111
|
"memorybody",
|
|
@@ -12592,11 +12127,11 @@ var FORBIDDEN_RAW_KEYS3 = new Set([
|
|
|
12592
12127
|
"transcriptbody",
|
|
12593
12128
|
"transcripttext"
|
|
12594
12129
|
]);
|
|
12595
|
-
var
|
|
12130
|
+
var SENSITIVE_TEXT_PATTERN4 = /https?:\/\/\S+|\b(secret|token|password|passwd|private|internal|api[_-]?key|apikey|credential|credentials|raw log|raw source|raw prompt)\b/i;
|
|
12596
12131
|
function createObservabilityEvent(input) {
|
|
12597
12132
|
const event = {
|
|
12598
12133
|
version: 1,
|
|
12599
|
-
eventId:
|
|
12134
|
+
eventId: sanitizeId2(input.eventId),
|
|
12600
12135
|
type: input.type,
|
|
12601
12136
|
timestamp: input.timestamp ?? new Date().toISOString(),
|
|
12602
12137
|
scope: input.scope,
|
|
@@ -12608,7 +12143,7 @@ function createObservabilityEvent(input) {
|
|
|
12608
12143
|
sourceContentStored: false,
|
|
12609
12144
|
promptStored: false
|
|
12610
12145
|
},
|
|
12611
|
-
summary:
|
|
12146
|
+
summary: sanitizeText2(input.summary),
|
|
12612
12147
|
data: sanitizeData(input.data ?? {})
|
|
12613
12148
|
};
|
|
12614
12149
|
validateObservabilityEvent(event);
|
|
@@ -12631,8 +12166,8 @@ function resolveObservabilityStorePaths(homeDir, type) {
|
|
|
12631
12166
|
async function appendObservabilityEvent(homeDir, event) {
|
|
12632
12167
|
validateObservabilityEvent(event);
|
|
12633
12168
|
const paths2 = resolveObservabilityStorePaths(homeDir, event.type);
|
|
12634
|
-
await
|
|
12635
|
-
await
|
|
12169
|
+
await mkdir11(dirname12(paths2.eventsPath), { recursive: true });
|
|
12170
|
+
await writeFile10(paths2.eventsPath, `${JSON.stringify(event)}
|
|
12636
12171
|
`, { encoding: "utf8", flag: "a" });
|
|
12637
12172
|
return paths2.eventsPath;
|
|
12638
12173
|
}
|
|
@@ -12641,7 +12176,7 @@ async function listObservabilityEvents(homeDir, type) {
|
|
|
12641
12176
|
const events = [];
|
|
12642
12177
|
for (const eventType of eventTypes) {
|
|
12643
12178
|
const path = resolveObservabilityStorePaths(homeDir, eventType).eventsPath;
|
|
12644
|
-
if (!await
|
|
12179
|
+
if (!await pathExists5(path))
|
|
12645
12180
|
continue;
|
|
12646
12181
|
const lines = (await readFile15(path, "utf8")).split(`
|
|
12647
12182
|
`).filter(Boolean);
|
|
@@ -12656,7 +12191,7 @@ async function listObservabilityEvents(homeDir, type) {
|
|
|
12656
12191
|
async function dryRunObservabilityRetentionCleanup(homeDir) {
|
|
12657
12192
|
const root = join18(homeDir, ".evodev", "OBSERVABILITY");
|
|
12658
12193
|
const candidates = [];
|
|
12659
|
-
if (!await
|
|
12194
|
+
if (!await pathExists5(root))
|
|
12660
12195
|
return { candidates, totalBytes: 0 };
|
|
12661
12196
|
for (const file of await collectJsonlFiles(root)) {
|
|
12662
12197
|
const fileStat = await stat8(file);
|
|
@@ -12689,13 +12224,13 @@ function sanitizeData(data) {
|
|
|
12689
12224
|
const sanitized = {};
|
|
12690
12225
|
for (const [key, value] of Object.entries(data)) {
|
|
12691
12226
|
const normalizedKey = normalizeKey(key);
|
|
12692
|
-
if (
|
|
12227
|
+
if (FORBIDDEN_RAW_KEYS2.has(normalizedKey)) {
|
|
12693
12228
|
throw new Error(`Observability data contains forbidden raw field: ${key}`);
|
|
12694
12229
|
}
|
|
12695
12230
|
if (typeof value === "string")
|
|
12696
|
-
sanitized[key] =
|
|
12231
|
+
sanitized[key] = sanitizeText2(value);
|
|
12697
12232
|
else if (Array.isArray(value))
|
|
12698
|
-
sanitized[key] = value.map(
|
|
12233
|
+
sanitized[key] = value.map(sanitizeText2);
|
|
12699
12234
|
else
|
|
12700
12235
|
sanitized[key] = value;
|
|
12701
12236
|
}
|
|
@@ -12705,7 +12240,7 @@ function assertNoForbiddenContent(value) {
|
|
|
12705
12240
|
if (typeof value === "string") {
|
|
12706
12241
|
if (value === "local-private")
|
|
12707
12242
|
return;
|
|
12708
|
-
if (
|
|
12243
|
+
if (SENSITIVE_TEXT_PATTERN4.test(value))
|
|
12709
12244
|
throw new Error("Observability event contains sensitive content.");
|
|
12710
12245
|
return;
|
|
12711
12246
|
}
|
|
@@ -12718,14 +12253,14 @@ function assertNoForbiddenContent(value) {
|
|
|
12718
12253
|
return;
|
|
12719
12254
|
for (const [key, child] of Object.entries(value)) {
|
|
12720
12255
|
const normalizedKey = normalizeKey(key);
|
|
12721
|
-
if (
|
|
12256
|
+
if (FORBIDDEN_RAW_KEYS2.has(normalizedKey)) {
|
|
12722
12257
|
throw new Error(`Observability event contains forbidden raw field: ${key}`);
|
|
12723
12258
|
}
|
|
12724
12259
|
assertNoForbiddenContent(child);
|
|
12725
12260
|
}
|
|
12726
12261
|
}
|
|
12727
12262
|
async function collectJsonlFiles(root) {
|
|
12728
|
-
const entries = await
|
|
12263
|
+
const entries = await readdir11(root, { withFileTypes: true });
|
|
12729
12264
|
const files = [];
|
|
12730
12265
|
for (const entry of entries) {
|
|
12731
12266
|
const path = join18(root, entry.name);
|
|
@@ -12736,7 +12271,7 @@ async function collectJsonlFiles(root) {
|
|
|
12736
12271
|
}
|
|
12737
12272
|
return files.sort();
|
|
12738
12273
|
}
|
|
12739
|
-
async function
|
|
12274
|
+
async function pathExists5(path) {
|
|
12740
12275
|
try {
|
|
12741
12276
|
await stat8(path);
|
|
12742
12277
|
return true;
|
|
@@ -12746,10 +12281,10 @@ async function pathExists6(path) {
|
|
|
12746
12281
|
throw error;
|
|
12747
12282
|
}
|
|
12748
12283
|
}
|
|
12749
|
-
function
|
|
12750
|
-
return value.replace(
|
|
12284
|
+
function sanitizeText2(value) {
|
|
12285
|
+
return value.replace(SENSITIVE_TEXT_PATTERN4, "[redacted]").slice(0, 500);
|
|
12751
12286
|
}
|
|
12752
|
-
function
|
|
12287
|
+
function sanitizeId2(value) {
|
|
12753
12288
|
return value.replace(/[^a-zA-Z0-9._-]/g, "-").slice(0, 80) || "event";
|
|
12754
12289
|
}
|
|
12755
12290
|
function normalizeKey(key) {
|
|
@@ -12758,7 +12293,7 @@ function normalizeKey(key) {
|
|
|
12758
12293
|
|
|
12759
12294
|
// packages/core/src/daemon/index.ts
|
|
12760
12295
|
var DEFAULT_PORT = 37645;
|
|
12761
|
-
var
|
|
12296
|
+
var SENSITIVE_TEXT_PATTERN5 = /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;
|
|
12762
12297
|
function resolveDaemonPaths(homeDir) {
|
|
12763
12298
|
const rootDir = join19(homeDir, ".evodev", "STATE", "daemon");
|
|
12764
12299
|
return { rootDir, lockPath: join19(rootDir, "lock.json"), tokenPath: join19(rootDir, "token") };
|
|
@@ -12801,14 +12336,14 @@ async function writeDaemonState(input) {
|
|
|
12801
12336
|
tokenPath: plan.paths.tokenPath,
|
|
12802
12337
|
versionText: input.versionText ?? "evodev 0.0.1-alpha"
|
|
12803
12338
|
};
|
|
12804
|
-
await
|
|
12805
|
-
await
|
|
12339
|
+
await mkdir12(dirname13(plan.paths.lockPath), { recursive: true });
|
|
12340
|
+
await writeFile11(plan.paths.tokenPath, `${token}
|
|
12806
12341
|
`, {
|
|
12807
12342
|
encoding: "utf8",
|
|
12808
12343
|
flag: "wx",
|
|
12809
12344
|
mode: 384
|
|
12810
12345
|
});
|
|
12811
|
-
await
|
|
12346
|
+
await writeFile11(plan.paths.lockPath, `${JSON.stringify(lock, null, 2)}
|
|
12812
12347
|
`, {
|
|
12813
12348
|
encoding: "utf8",
|
|
12814
12349
|
flag: "wx",
|
|
@@ -12818,7 +12353,7 @@ async function writeDaemonState(input) {
|
|
|
12818
12353
|
}
|
|
12819
12354
|
async function readDaemonLock(homeDir) {
|
|
12820
12355
|
const paths2 = resolveDaemonPaths(homeDir);
|
|
12821
|
-
if (!await
|
|
12356
|
+
if (!await pathExists6(paths2.lockPath))
|
|
12822
12357
|
return null;
|
|
12823
12358
|
const lock = JSON.parse(await readFile16(paths2.lockPath, "utf8"));
|
|
12824
12359
|
if (lock.version !== 1 || lock.component !== "evodev-daemon")
|
|
@@ -12827,7 +12362,7 @@ async function readDaemonLock(homeDir) {
|
|
|
12827
12362
|
}
|
|
12828
12363
|
async function readDaemonToken(homeDir) {
|
|
12829
12364
|
const paths2 = resolveDaemonPaths(homeDir);
|
|
12830
|
-
if (!await
|
|
12365
|
+
if (!await pathExists6(paths2.tokenPath))
|
|
12831
12366
|
return null;
|
|
12832
12367
|
return (await readFile16(paths2.tokenPath, "utf8")).trim();
|
|
12833
12368
|
}
|
|
@@ -12891,8 +12426,6 @@ async function handleDaemonRequest(input) {
|
|
|
12891
12426
|
}
|
|
12892
12427
|
if (input.method !== "GET")
|
|
12893
12428
|
return notFound(warnings);
|
|
12894
|
-
if (input.path === "/tasks")
|
|
12895
|
-
return ok(await collectTaskSummaries(input.homeDir, warnings), warnings);
|
|
12896
12429
|
if (input.path === "/observability/events")
|
|
12897
12430
|
return ok(await collectObservabilitySummaries(input.homeDir, warnings), warnings);
|
|
12898
12431
|
if (input.path === "/memory/candidates")
|
|
@@ -12942,14 +12475,14 @@ async function runDaemonForeground(input) {
|
|
|
12942
12475
|
}));
|
|
12943
12476
|
}
|
|
12944
12477
|
});
|
|
12945
|
-
await new Promise((
|
|
12478
|
+
await new Promise((resolve4, reject) => {
|
|
12946
12479
|
const onError = (error) => {
|
|
12947
12480
|
server.off("listening", onListening);
|
|
12948
12481
|
reject(error);
|
|
12949
12482
|
};
|
|
12950
12483
|
const onListening = () => {
|
|
12951
12484
|
server.off("error", onError);
|
|
12952
|
-
|
|
12485
|
+
resolve4();
|
|
12953
12486
|
};
|
|
12954
12487
|
server.once("error", onError);
|
|
12955
12488
|
server.once("listening", onListening);
|
|
@@ -12966,11 +12499,11 @@ async function runDaemonForeground(input) {
|
|
|
12966
12499
|
processEvolutionTriggers({ homeDir: input.homeDir, limit: 20 }).then(() => clearDaemonEvolutionProcessError(input.homeDir)).catch((error) => recordDaemonEvolutionProcessError(input.homeDir, error));
|
|
12967
12500
|
}, 5000);
|
|
12968
12501
|
evolutionInterval.unref();
|
|
12969
|
-
await new Promise((
|
|
12502
|
+
await new Promise((resolve4, reject) => {
|
|
12970
12503
|
server.once("close", () => {
|
|
12971
12504
|
clearInterval(reconcileInterval);
|
|
12972
12505
|
clearInterval(evolutionInterval);
|
|
12973
|
-
|
|
12506
|
+
resolve4();
|
|
12974
12507
|
});
|
|
12975
12508
|
server.once("error", reject);
|
|
12976
12509
|
});
|
|
@@ -13017,30 +12550,6 @@ function isAllowedLocalOrigin(origin) {
|
|
|
13017
12550
|
return false;
|
|
13018
12551
|
}
|
|
13019
12552
|
}
|
|
13020
|
-
async function collectTaskSummaries(homeDir, warnings) {
|
|
13021
|
-
const root = join19(homeDir, ".evodev", "STATE", "tasks");
|
|
13022
|
-
if (!await pathExists7(root)) {
|
|
13023
|
-
warnings.push("Task store not found; returning empty tasks.");
|
|
13024
|
-
return [];
|
|
13025
|
-
}
|
|
13026
|
-
const contracts = await collectNamedFiles(root, "contract.json");
|
|
13027
|
-
const summaries = [];
|
|
13028
|
-
for (const file of contracts) {
|
|
13029
|
-
try {
|
|
13030
|
-
const contract = JSON.parse(await readFile16(file, "utf8"));
|
|
13031
|
-
summaries.push(sanitizeMetadata({
|
|
13032
|
-
taskId: contract.taskId,
|
|
13033
|
-
status: contract.status,
|
|
13034
|
-
mode: contract.route?.mode ?? null,
|
|
13035
|
-
workflowId: contract.route?.workflowId ?? null,
|
|
13036
|
-
verificationStatus: contract.verification?.status ?? null
|
|
13037
|
-
}));
|
|
13038
|
-
} catch {
|
|
13039
|
-
warnings.push(`Skipped unreadable task contract: ${file}`);
|
|
13040
|
-
}
|
|
13041
|
-
}
|
|
13042
|
-
return summaries;
|
|
13043
|
-
}
|
|
13044
12553
|
async function collectObservabilitySummaries(homeDir, warnings) {
|
|
13045
12554
|
try {
|
|
13046
12555
|
return (await listObservabilityEvents(homeDir)).map((event) => sanitizeMetadata({
|
|
@@ -13057,7 +12566,7 @@ async function collectObservabilitySummaries(homeDir, warnings) {
|
|
|
13057
12566
|
}
|
|
13058
12567
|
async function collectLearningCandidateSummaries(homeDir, warnings) {
|
|
13059
12568
|
const path = join19(homeDir, ".evodev", "STATE", "learning", "candidates.jsonl");
|
|
13060
|
-
if (!await
|
|
12569
|
+
if (!await pathExists6(path)) {
|
|
13061
12570
|
warnings.push("Learning candidate store not found; returning empty candidates.");
|
|
13062
12571
|
return [];
|
|
13063
12572
|
}
|
|
@@ -13289,16 +12798,16 @@ async function resumeDashboardTeamRun(homeDir, body, runtimeAdapter) {
|
|
|
13289
12798
|
};
|
|
13290
12799
|
}
|
|
13291
12800
|
async function collectDirectorySummaries(root, warnings) {
|
|
13292
|
-
if (!await
|
|
12801
|
+
if (!await pathExists6(root)) {
|
|
13293
12802
|
warnings.push(`Store not found: ${root}`);
|
|
13294
12803
|
return [];
|
|
13295
12804
|
}
|
|
13296
|
-
const entries = await
|
|
12805
|
+
const entries = await readdir12(root, { withFileTypes: true });
|
|
13297
12806
|
return entries.filter((entry) => entry.isDirectory()).map((entry) => ({ id: entry.name, metadataOnly: true }));
|
|
13298
12807
|
}
|
|
13299
12808
|
function sanitizeMetadata(value) {
|
|
13300
12809
|
if (typeof value === "string") {
|
|
13301
|
-
if (
|
|
12810
|
+
if (SENSITIVE_TEXT_PATTERN5.test(value))
|
|
13302
12811
|
return "[redacted]";
|
|
13303
12812
|
return value.slice(0, 500);
|
|
13304
12813
|
}
|
|
@@ -13349,8 +12858,8 @@ function resolveDaemonEvolutionProcessErrorPath(homeDir) {
|
|
|
13349
12858
|
async function recordDaemonEvolutionProcessError(homeDir, error) {
|
|
13350
12859
|
try {
|
|
13351
12860
|
const path = resolveDaemonEvolutionProcessErrorPath(homeDir);
|
|
13352
|
-
await
|
|
13353
|
-
await
|
|
12861
|
+
await mkdir12(dirname13(path), { recursive: true });
|
|
12862
|
+
await writeFile11(path, `${JSON.stringify({
|
|
13354
12863
|
schemaVersion: 1,
|
|
13355
12864
|
kind: "daemon-evolution-process-error",
|
|
13356
12865
|
updatedAt: new Date().toISOString(),
|
|
@@ -13366,7 +12875,7 @@ async function clearDaemonEvolutionProcessError(homeDir) {
|
|
|
13366
12875
|
}
|
|
13367
12876
|
async function readDaemonEvolutionProcessError(homeDir) {
|
|
13368
12877
|
const path = resolveDaemonEvolutionProcessErrorPath(homeDir);
|
|
13369
|
-
if (!await
|
|
12878
|
+
if (!await pathExists6(path))
|
|
13370
12879
|
return null;
|
|
13371
12880
|
const value = JSON.parse(await readFile16(path, "utf8"));
|
|
13372
12881
|
return typeof value.summary === "string" && value.summary.length > 0 ? value.summary : null;
|
|
@@ -13374,25 +12883,13 @@ async function readDaemonEvolutionProcessError(homeDir) {
|
|
|
13374
12883
|
function describeError3(error) {
|
|
13375
12884
|
return error instanceof Error ? error.message : String(error);
|
|
13376
12885
|
}
|
|
13377
|
-
async function collectNamedFiles(root, name) {
|
|
13378
|
-
const entries = await readdir11(root, { withFileTypes: true });
|
|
13379
|
-
const files = [];
|
|
13380
|
-
for (const entry of entries) {
|
|
13381
|
-
const path = join19(root, entry.name);
|
|
13382
|
-
if (entry.isDirectory())
|
|
13383
|
-
files.push(...await collectNamedFiles(path, name));
|
|
13384
|
-
else if (entry.isFile() && entry.name === name)
|
|
13385
|
-
files.push(path);
|
|
13386
|
-
}
|
|
13387
|
-
return files;
|
|
13388
|
-
}
|
|
13389
12886
|
function ok(data, warnings) {
|
|
13390
12887
|
return { status: 200, body: { ok: true, data: sanitizeMetadata(data), warnings } };
|
|
13391
12888
|
}
|
|
13392
12889
|
function notFound(warnings) {
|
|
13393
12890
|
return { status: 404, body: { ok: false, data: { error: "not found" }, warnings } };
|
|
13394
12891
|
}
|
|
13395
|
-
async function
|
|
12892
|
+
async function pathExists6(path) {
|
|
13396
12893
|
try {
|
|
13397
12894
|
await stat9(path);
|
|
13398
12895
|
return true;
|
|
@@ -13418,7 +12915,7 @@ __export(exports_review, {
|
|
|
13418
12915
|
resolveLearningCandidateQueuePath: () => resolveLearningCandidateQueuePath,
|
|
13419
12916
|
readLearningReviewDecisions: () => readLearningReviewDecisions,
|
|
13420
12917
|
readLearningCandidates: () => readLearningCandidates,
|
|
13421
|
-
pathExists: () =>
|
|
12918
|
+
pathExists: () => pathExists7,
|
|
13422
12919
|
parseLearningReviewDecisionRecord: () => parseLearningReviewDecisionRecord,
|
|
13423
12920
|
parseLearningCandidate: () => parseLearningCandidate,
|
|
13424
12921
|
listLearningReviewDecisions: () => listLearningReviewDecisions,
|
|
@@ -13432,7 +12929,7 @@ __export(exports_review, {
|
|
|
13432
12929
|
appendLearningReviewDecision: () => appendLearningReviewDecision,
|
|
13433
12930
|
appendLearningCandidate: () => appendLearningCandidate
|
|
13434
12931
|
});
|
|
13435
|
-
import { mkdir as
|
|
12932
|
+
import { mkdir as mkdir13, readFile as readFile17, stat as stat10, writeFile as writeFile12 } from "node:fs/promises";
|
|
13436
12933
|
import { dirname as dirname14, join as join20 } from "node:path";
|
|
13437
12934
|
var LEARNING_CANDIDATE_KINDS = [
|
|
13438
12935
|
"lesson",
|
|
@@ -13455,7 +12952,7 @@ var LEARNING_CONFIDENCE_VALUES = ["low", "medium", "high"];
|
|
|
13455
12952
|
var LEARNING_DECISION_VALUES = ["rejected", "deferred"];
|
|
13456
12953
|
var LEARNING_DECIDED_BY_VALUES = ["user"];
|
|
13457
12954
|
var LEARNING_CANDIDATE_DECISION_STATUSES = ["rejected", "deferred"];
|
|
13458
|
-
var
|
|
12955
|
+
var FORBIDDEN_RAW_KEYS3 = new Set([
|
|
13459
12956
|
"commandhistory",
|
|
13460
12957
|
"commandoutput",
|
|
13461
12958
|
"credential",
|
|
@@ -13490,28 +12987,27 @@ var FORBIDDEN_RAW_KEYS4 = new Set([
|
|
|
13490
12987
|
"transcriptbody",
|
|
13491
12988
|
"transcripttext"
|
|
13492
12989
|
]);
|
|
13493
|
-
var
|
|
12990
|
+
var SENSITIVE_TEXT_PATTERN6 = /https?:\/\/\S+|\b(secret|token|password|passwd|private|internal|api[_-]?key|apikey|credential|credentials|raw log|raw logs|raw output|raw source|raw prompt|shell history|command history)\b/i;
|
|
13494
12991
|
var PROTECTED_PATH_PATTERN = /(^|[~/\\])(?:USER|KNOWLEDGE|LEARNING|OBSERVABILITY|PACKS|RELEASES|logs?|memory|\.env[^/\\]*)(?:$|[/\\])|PROJECTS[/\\][^/\\]+[/\\]LEARNING(?:$|[/\\])|\.evodev[/\\](?:USER|KNOWLEDGE|LEARNING|OBSERVABILITY|PACKS|RELEASES)(?:$|[/\\])/i;
|
|
13495
12992
|
function createLearningCandidate(input) {
|
|
13496
12993
|
const candidate = {
|
|
13497
12994
|
version: 1,
|
|
13498
|
-
id:
|
|
12995
|
+
id: sanitizeId3(input.id),
|
|
13499
12996
|
kind: input.kind,
|
|
13500
12997
|
status: "candidate",
|
|
13501
12998
|
routingInfluence: false,
|
|
13502
12999
|
scope: sanitizeScope(input.scope),
|
|
13503
13000
|
content: {
|
|
13504
|
-
summary:
|
|
13505
|
-
howToApply:
|
|
13506
|
-
antiCriteriaImpact: input.content.antiCriteriaImpact.map(
|
|
13001
|
+
summary: sanitizeText3(input.content.summary),
|
|
13002
|
+
howToApply: sanitizeText3(input.content.howToApply),
|
|
13003
|
+
antiCriteriaImpact: input.content.antiCriteriaImpact.map(sanitizeText3)
|
|
13507
13004
|
},
|
|
13508
13005
|
provenance: {
|
|
13509
13006
|
taskId: sanitizeNullableId(input.provenance.taskId),
|
|
13510
|
-
taskContractRef: sanitizeNullableText(input.provenance.taskContractRef),
|
|
13511
13007
|
workflowRunId: sanitizeNullableId(input.provenance.workflowRunId),
|
|
13512
|
-
evidenceRefs: input.provenance.evidenceRefs.map(
|
|
13008
|
+
evidenceRefs: input.provenance.evidenceRefs.map(sanitizeText3),
|
|
13513
13009
|
sourceType: input.provenance.sourceType,
|
|
13514
|
-
createdAt:
|
|
13010
|
+
createdAt: sanitizeText3(input.provenance.createdAt),
|
|
13515
13011
|
createdBy: input.provenance.createdBy,
|
|
13516
13012
|
rawPromptStored: false,
|
|
13517
13013
|
sourceContentStored: false,
|
|
@@ -13530,7 +13026,7 @@ function createLearningCandidate(input) {
|
|
|
13530
13026
|
return candidate;
|
|
13531
13027
|
}
|
|
13532
13028
|
function validateLearningCandidate(candidate) {
|
|
13533
|
-
if (!
|
|
13029
|
+
if (!isRecord10(candidate))
|
|
13534
13030
|
throw new Error("Learning candidate must be an object.");
|
|
13535
13031
|
if (candidate.version !== 1)
|
|
13536
13032
|
throw new Error("Learning candidate version must be 1.");
|
|
@@ -13542,33 +13038,33 @@ function validateLearningCandidate(candidate) {
|
|
|
13542
13038
|
if (candidate.routingInfluence !== false) {
|
|
13543
13039
|
throw new Error("Learning candidate routingInfluence must be false in I5.");
|
|
13544
13040
|
}
|
|
13545
|
-
if (!
|
|
13041
|
+
if (!isRecord10(candidate.scope))
|
|
13546
13042
|
throw new Error("Learning candidate scope must be an object.");
|
|
13547
13043
|
assertEnumValue("scope.level", candidate.scope.level, LEARNING_SCOPE_LEVELS);
|
|
13548
|
-
if (!
|
|
13044
|
+
if (!isRecord10(candidate.content)) {
|
|
13549
13045
|
throw new Error("Learning candidate content must be an object.");
|
|
13550
13046
|
}
|
|
13551
13047
|
assertStringField("content.summary", candidate.content.summary);
|
|
13552
13048
|
assertStringField("content.howToApply", candidate.content.howToApply);
|
|
13553
13049
|
assertStringArrayField("content.antiCriteriaImpact", candidate.content.antiCriteriaImpact);
|
|
13554
|
-
if (!
|
|
13050
|
+
if (!isRecord10(candidate.provenance)) {
|
|
13555
13051
|
throw new Error("Learning candidate provenance must be an object.");
|
|
13556
13052
|
}
|
|
13557
13053
|
assertEnumValue("provenance.sourceType", candidate.provenance.sourceType, LEARNING_SOURCE_TYPES);
|
|
13558
13054
|
assertEnumValue("provenance.createdBy", candidate.provenance.createdBy, LEARNING_CREATED_BY_VALUES);
|
|
13559
13055
|
assertStringField("provenance.createdAt", candidate.provenance.createdAt);
|
|
13560
13056
|
assertStringArrayField("provenance.evidenceRefs", candidate.provenance.evidenceRefs);
|
|
13561
|
-
if (!
|
|
13057
|
+
if (!isRecord10(candidate.privacy)) {
|
|
13562
13058
|
throw new Error("Learning candidate privacy must be an object.");
|
|
13563
13059
|
}
|
|
13564
13060
|
assertEnumValue("privacy.classification", candidate.privacy.classification, ["local-private"]);
|
|
13565
|
-
if (!
|
|
13061
|
+
if (!isRecord10(candidate.review))
|
|
13566
13062
|
throw new Error("Learning candidate review must be an object.");
|
|
13567
13063
|
assertEnumValue("review.decision", candidate.review.decision, LEARNING_REVIEW_DECISIONS);
|
|
13568
13064
|
if (candidate.review.decision !== "pending") {
|
|
13569
13065
|
throw new Error("Learning candidate review decision must be pending.");
|
|
13570
13066
|
}
|
|
13571
|
-
if (!
|
|
13067
|
+
if (!isRecord10(candidate.retention)) {
|
|
13572
13068
|
throw new Error("Learning candidate retention must be an object.");
|
|
13573
13069
|
}
|
|
13574
13070
|
assertEnumValue("confidence", candidate.confidence, LEARNING_CONFIDENCE_VALUES);
|
|
@@ -13584,7 +13080,7 @@ function validateLearningCandidate(candidate) {
|
|
|
13584
13080
|
assertNoForbiddenContent2(candidate);
|
|
13585
13081
|
}
|
|
13586
13082
|
function validateLearningReviewDecisionRecord(record) {
|
|
13587
|
-
if (!
|
|
13083
|
+
if (!isRecord10(record))
|
|
13588
13084
|
throw new Error("Learning review decision must be an object.");
|
|
13589
13085
|
if (record.version !== 1)
|
|
13590
13086
|
throw new Error("Learning review decision version must be 1.");
|
|
@@ -13607,13 +13103,13 @@ function validateLearningReviewDecisionRecord(record) {
|
|
|
13607
13103
|
assertNoForbiddenContent2(record);
|
|
13608
13104
|
}
|
|
13609
13105
|
function parseLearningCandidate(value) {
|
|
13610
|
-
if (!
|
|
13106
|
+
if (!isRecord10(value))
|
|
13611
13107
|
throw new Error("Invalid learning candidate JSON.");
|
|
13612
13108
|
validateLearningCandidate(value);
|
|
13613
13109
|
return value;
|
|
13614
13110
|
}
|
|
13615
13111
|
function parseLearningReviewDecisionRecord(value) {
|
|
13616
|
-
if (!
|
|
13112
|
+
if (!isRecord10(value))
|
|
13617
13113
|
throw new Error("Invalid learning review decision JSON.");
|
|
13618
13114
|
validateLearningReviewDecisionRecord(value);
|
|
13619
13115
|
return value;
|
|
@@ -13633,20 +13129,20 @@ function resolveLearningDecisionPath(homeDir) {
|
|
|
13633
13129
|
async function appendLearningCandidate(homeDir, candidate) {
|
|
13634
13130
|
validateLearningCandidate(candidate);
|
|
13635
13131
|
const path = resolveLearningCandidateQueuePath(homeDir);
|
|
13636
|
-
await
|
|
13637
|
-
await
|
|
13132
|
+
await mkdir13(dirname14(path), { recursive: true });
|
|
13133
|
+
await writeFile12(path, `${JSON.stringify(candidate)}
|
|
13638
13134
|
`, { encoding: "utf8", flag: "a" });
|
|
13639
13135
|
return path;
|
|
13640
13136
|
}
|
|
13641
13137
|
async function listLearningCandidates(homeDir) {
|
|
13642
13138
|
const path = resolveLearningCandidateQueuePath(homeDir);
|
|
13643
|
-
if (!await
|
|
13139
|
+
if (!await pathExists7(path))
|
|
13644
13140
|
return [];
|
|
13645
13141
|
return readLearningCandidates(path);
|
|
13646
13142
|
}
|
|
13647
13143
|
async function listLearningReviewDecisions(homeDir) {
|
|
13648
13144
|
const path = resolveLearningDecisionPath(homeDir);
|
|
13649
|
-
if (!await
|
|
13145
|
+
if (!await pathExists7(path))
|
|
13650
13146
|
return [];
|
|
13651
13147
|
return readLearningReviewDecisions(path);
|
|
13652
13148
|
}
|
|
@@ -13706,7 +13202,7 @@ function lintLearningCandidates(candidates2, options = {}) {
|
|
|
13706
13202
|
validateLearningReviewDecisionRecord(decision);
|
|
13707
13203
|
} catch (error) {
|
|
13708
13204
|
findings.push({
|
|
13709
|
-
candidateId:
|
|
13205
|
+
candidateId: isRecord10(decision) && typeof decision.candidateId === "string" ? decision.candidateId : "unknown",
|
|
13710
13206
|
severity: "error",
|
|
13711
13207
|
field: "decision",
|
|
13712
13208
|
message: error instanceof Error ? error.message : String(error)
|
|
@@ -13778,9 +13274,9 @@ function createLearningReviewDecisionRecord(input) {
|
|
|
13778
13274
|
const candidateStatus = input.decision === "rejected" ? "rejected" : "deferred";
|
|
13779
13275
|
const record = {
|
|
13780
13276
|
version: 1,
|
|
13781
|
-
candidateId:
|
|
13277
|
+
candidateId: sanitizeId3(input.candidateId),
|
|
13782
13278
|
decision: input.decision,
|
|
13783
|
-
decidedAt:
|
|
13279
|
+
decidedAt: sanitizeText3(input.decidedAt ?? new Date().toISOString()),
|
|
13784
13280
|
decidedBy: "user",
|
|
13785
13281
|
reason: input.reason === undefined ? null : sanitizeNullableText(input.reason),
|
|
13786
13282
|
writesAcceptedMemory: false,
|
|
@@ -13793,12 +13289,12 @@ function createLearningReviewDecisionRecord(input) {
|
|
|
13793
13289
|
async function appendLearningReviewDecision(homeDir, record) {
|
|
13794
13290
|
validateLearningReviewDecisionRecord(record);
|
|
13795
13291
|
const path = resolveLearningDecisionPath(homeDir);
|
|
13796
|
-
await
|
|
13797
|
-
await
|
|
13292
|
+
await mkdir13(dirname14(path), { recursive: true });
|
|
13293
|
+
await writeFile12(path, `${JSON.stringify(record)}
|
|
13798
13294
|
`, { encoding: "utf8", flag: "a" });
|
|
13799
13295
|
return path;
|
|
13800
13296
|
}
|
|
13801
|
-
async function
|
|
13297
|
+
async function pathExists7(path) {
|
|
13802
13298
|
try {
|
|
13803
13299
|
await stat10(path);
|
|
13804
13300
|
return true;
|
|
@@ -13860,13 +13356,7 @@ async function parseJsonOrJsonlFile(path, parseItem) {
|
|
|
13860
13356
|
return values.map(parseItem);
|
|
13861
13357
|
}
|
|
13862
13358
|
function candidatePathFields(candidate) {
|
|
13863
|
-
return [
|
|
13864
|
-
["provenance.taskContractRef", candidate.provenance.taskContractRef],
|
|
13865
|
-
...candidate.provenance.evidenceRefs.map((ref, index) => [
|
|
13866
|
-
`provenance.evidenceRefs[${index}]`,
|
|
13867
|
-
ref
|
|
13868
|
-
])
|
|
13869
|
-
].filter((entry) => typeof entry[1] === "string");
|
|
13359
|
+
return candidate.provenance.evidenceRefs.map((ref, index) => [`provenance.evidenceRefs[${index}]`, ref]).filter((entry) => typeof entry[1] === "string");
|
|
13870
13360
|
}
|
|
13871
13361
|
function isCandidateStale(candidate, now) {
|
|
13872
13362
|
if (candidate.retention.staleAfter === null)
|
|
@@ -13894,7 +13384,7 @@ function assertNoForbiddenContent2(value) {
|
|
|
13894
13384
|
if (typeof value === "string") {
|
|
13895
13385
|
if (value === "local-private")
|
|
13896
13386
|
return;
|
|
13897
|
-
if (
|
|
13387
|
+
if (SENSITIVE_TEXT_PATTERN6.test(value)) {
|
|
13898
13388
|
throw new Error("Learning candidate contains sensitive content.");
|
|
13899
13389
|
}
|
|
13900
13390
|
return;
|
|
@@ -13904,38 +13394,38 @@ function assertNoForbiddenContent2(value) {
|
|
|
13904
13394
|
assertNoForbiddenContent2(item);
|
|
13905
13395
|
return;
|
|
13906
13396
|
}
|
|
13907
|
-
if (!
|
|
13397
|
+
if (!isRecord10(value))
|
|
13908
13398
|
return;
|
|
13909
13399
|
for (const [key, child] of Object.entries(value)) {
|
|
13910
13400
|
const normalizedKey = normalizeKey2(key);
|
|
13911
|
-
if (
|
|
13401
|
+
if (FORBIDDEN_RAW_KEYS3.has(normalizedKey)) {
|
|
13912
13402
|
throw new Error(`Learning candidate contains forbidden raw field: ${key}`);
|
|
13913
13403
|
}
|
|
13914
13404
|
assertNoForbiddenContent2(child);
|
|
13915
13405
|
}
|
|
13916
13406
|
}
|
|
13917
|
-
function
|
|
13918
|
-
return value.replace(
|
|
13407
|
+
function sanitizeText3(value) {
|
|
13408
|
+
return value.replace(SENSITIVE_TEXT_PATTERN6, "[redacted]").slice(0, 500);
|
|
13919
13409
|
}
|
|
13920
13410
|
function sanitizeNullableText(value) {
|
|
13921
|
-
return value === null ? null :
|
|
13411
|
+
return value === null ? null : sanitizeText3(value);
|
|
13922
13412
|
}
|
|
13923
|
-
function
|
|
13924
|
-
const sanitized =
|
|
13413
|
+
function sanitizeId3(value) {
|
|
13414
|
+
const sanitized = sanitizeText3(value).replace(/\[redacted\]/gi, "redacted").replace(/[^a-zA-Z0-9._-]/g, "-").replace(/-+/g, "-").replace(/^-+|-+$/g, "").slice(0, 100);
|
|
13925
13415
|
return sanitized || "learning-candidate";
|
|
13926
13416
|
}
|
|
13927
13417
|
function sanitizeNullableId(value) {
|
|
13928
|
-
return value === null ? null :
|
|
13418
|
+
return value === null ? null : sanitizeId3(value);
|
|
13929
13419
|
}
|
|
13930
13420
|
function normalizeKey2(key) {
|
|
13931
13421
|
return key.toLowerCase().replace(/[^a-z0-9]/g, "");
|
|
13932
13422
|
}
|
|
13933
|
-
function
|
|
13423
|
+
function isRecord10(value) {
|
|
13934
13424
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
13935
13425
|
}
|
|
13936
13426
|
// packages/core/src/pack/index.ts
|
|
13937
|
-
import { readFile as readFile18, readdir as
|
|
13938
|
-
import { isAbsolute as
|
|
13427
|
+
import { readFile as readFile18, readdir as readdir13, stat as stat11 } from "node:fs/promises";
|
|
13428
|
+
import { isAbsolute as isAbsolute7, join as join21, relative as relative7, sep } from "node:path";
|
|
13939
13429
|
|
|
13940
13430
|
// packages/core/src/protected-zones/index.ts
|
|
13941
13431
|
var SENSITIVE_DIRECTORY_SEGMENTS = new Set([
|
|
@@ -14170,7 +13660,7 @@ async function planPackInstallDryRun(input) {
|
|
|
14170
13660
|
};
|
|
14171
13661
|
}
|
|
14172
13662
|
function parsePackManifest(value) {
|
|
14173
|
-
if (!
|
|
13663
|
+
if (!isRecord11(value))
|
|
14174
13664
|
throw new Error("Pack manifest must be an object.");
|
|
14175
13665
|
const manifest = value;
|
|
14176
13666
|
validateManifestTopLevelFields(manifest);
|
|
@@ -14180,14 +13670,14 @@ function parsePackManifest(value) {
|
|
|
14180
13670
|
const description = requireString(manifest, "description");
|
|
14181
13671
|
const publisher = optionalString6(manifest, "publisher");
|
|
14182
13672
|
const license = optionalString6(manifest, "license");
|
|
14183
|
-
const compatibility =
|
|
14184
|
-
const assets = parseAssets(
|
|
13673
|
+
const compatibility = requireRecord2(manifest, "compatibility");
|
|
13674
|
+
const assets = parseAssets(requireRecord2(manifest, "assets"));
|
|
14185
13675
|
const permissions = parsePermissions(manifest.permissions);
|
|
14186
|
-
const install =
|
|
14187
|
-
const verify =
|
|
14188
|
-
const customizations =
|
|
14189
|
-
const observability =
|
|
14190
|
-
const uninstall =
|
|
13676
|
+
const install = requireRecord2(manifest, "install");
|
|
13677
|
+
const verify = requireRecord2(manifest, "verify");
|
|
13678
|
+
const customizations = requireRecord2(manifest, "customizations");
|
|
13679
|
+
const observability = requireRecord2(manifest, "observability");
|
|
13680
|
+
const uninstall = requireRecord2(manifest, "uninstall");
|
|
14191
13681
|
validateCompatibilityTargets(id, requireStringArray(compatibility, "targets"));
|
|
14192
13682
|
validateProtectedZoneDeclarations(manifest.protectedZones);
|
|
14193
13683
|
if (observability.metadataOnly !== true) {
|
|
@@ -14225,7 +13715,7 @@ function parsePackManifest(value) {
|
|
|
14225
13715
|
userPath: requireString(customizations, "userPath"),
|
|
14226
13716
|
projectPath: requireString(customizations, "projectPath")
|
|
14227
13717
|
},
|
|
14228
|
-
protectedZones:
|
|
13718
|
+
protectedZones: isRecord11(manifest.protectedZones) ? {
|
|
14229
13719
|
neverInclude: optionalStringArray(manifest.protectedZones, "neverInclude"),
|
|
14230
13720
|
neverWrite: optionalStringArray(manifest.protectedZones, "neverWrite")
|
|
14231
13721
|
} : undefined,
|
|
@@ -14415,7 +13905,7 @@ function validateGuideReferences(manifest, assets, findings) {
|
|
|
14415
13905
|
}
|
|
14416
13906
|
}
|
|
14417
13907
|
async function collectPackRelativePaths(packRoot, dir = packRoot) {
|
|
14418
|
-
const entries = await
|
|
13908
|
+
const entries = await readdir13(dir, { withFileTypes: true });
|
|
14419
13909
|
const paths3 = [];
|
|
14420
13910
|
for (const entry of entries) {
|
|
14421
13911
|
const absolutePath = join21(dir, entry.name);
|
|
@@ -14434,7 +13924,7 @@ function validatePackRelativePath(path, packRoot) {
|
|
|
14434
13924
|
if (path.includes("\x00")) {
|
|
14435
13925
|
return { severity: "error", code: "path-invalid", message: "Path must not contain NUL bytes." };
|
|
14436
13926
|
}
|
|
14437
|
-
if (
|
|
13927
|
+
if (isAbsolute7(path) || path.startsWith("~")) {
|
|
14438
13928
|
return {
|
|
14439
13929
|
severity: "error",
|
|
14440
13930
|
code: "path-absolute",
|
|
@@ -14451,7 +13941,7 @@ function validatePackRelativePath(path, packRoot) {
|
|
|
14451
13941
|
}
|
|
14452
13942
|
const absolute = join21(packRoot, normalized);
|
|
14453
13943
|
const rel = relative7(packRoot, absolute);
|
|
14454
|
-
if (rel === "" || rel.startsWith("..") ||
|
|
13944
|
+
if (rel === "" || rel.startsWith("..") || isAbsolute7(rel)) {
|
|
14455
13945
|
return {
|
|
14456
13946
|
severity: "error",
|
|
14457
13947
|
code: "path-traversal",
|
|
@@ -14496,7 +13986,7 @@ function parseAssets(value) {
|
|
|
14496
13986
|
function parsePermissions(value) {
|
|
14497
13987
|
if (value === undefined)
|
|
14498
13988
|
return {};
|
|
14499
|
-
if (!
|
|
13989
|
+
if (!isRecord11(value))
|
|
14500
13990
|
throw new Error("Pack manifest permissions must be an object.");
|
|
14501
13991
|
const permissions = {};
|
|
14502
13992
|
const knownKeys = new Set(Object.keys(RISKY_PERMISSION_LABELS));
|
|
@@ -14532,9 +14022,9 @@ function requireBoolean(record, key) {
|
|
|
14532
14022
|
throw new Error(`Pack manifest missing boolean field: ${key}`);
|
|
14533
14023
|
return value;
|
|
14534
14024
|
}
|
|
14535
|
-
function
|
|
14025
|
+
function requireRecord2(record, key) {
|
|
14536
14026
|
const value = record[key];
|
|
14537
|
-
if (!
|
|
14027
|
+
if (!isRecord11(value))
|
|
14538
14028
|
throw new Error(`Pack manifest missing object field: ${key}`);
|
|
14539
14029
|
return value;
|
|
14540
14030
|
}
|
|
@@ -14590,7 +14080,7 @@ function validateCompatibilityTargets(packId, targets) {
|
|
|
14590
14080
|
function validateProtectedZoneDeclarations(value) {
|
|
14591
14081
|
if (value === undefined)
|
|
14592
14082
|
return;
|
|
14593
|
-
if (!
|
|
14083
|
+
if (!isRecord11(value))
|
|
14594
14084
|
throw new Error("Pack manifest protectedZones must be an object.");
|
|
14595
14085
|
const knownFields = new Set(["neverInclude", "neverWrite"]);
|
|
14596
14086
|
for (const key of Object.keys(value)) {
|
|
@@ -14603,7 +14093,7 @@ function validateProtectedZoneDeclarations(value) {
|
|
|
14603
14093
|
function isRemotePackInput(packPath) {
|
|
14604
14094
|
return /^[a-z][a-z0-9+.-]*:\/\//i.test(packPath);
|
|
14605
14095
|
}
|
|
14606
|
-
function
|
|
14096
|
+
function isRecord11(value) {
|
|
14607
14097
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
14608
14098
|
}
|
|
14609
14099
|
function normalizeRelativePath(path) {
|
|
@@ -14616,7 +14106,7 @@ function formatError(error) {
|
|
|
14616
14106
|
return error instanceof Error ? error.message : String(error);
|
|
14617
14107
|
}
|
|
14618
14108
|
// packages/core/src/plugins/capabilities.ts
|
|
14619
|
-
import { mkdir as
|
|
14109
|
+
import { mkdir as mkdir14, readFile as readFile19, writeFile as writeFile13 } from "node:fs/promises";
|
|
14620
14110
|
import { dirname as dirname15, join as join22 } from "node:path";
|
|
14621
14111
|
function createUnknownNegotiatedCapabilities(pluginId) {
|
|
14622
14112
|
return {
|
|
@@ -14669,7 +14159,7 @@ function resolveNonCodexCapabilityState(supported, enabled) {
|
|
|
14669
14159
|
}
|
|
14670
14160
|
async function createCodexCapabilityVerificationArtifact(input) {
|
|
14671
14161
|
const timestamp = input.createdAt ?? new Date().toISOString();
|
|
14672
|
-
const detectionMessage =
|
|
14162
|
+
const detectionMessage = sanitizeText4(input.detection.message);
|
|
14673
14163
|
const diagnostics = [
|
|
14674
14164
|
"Codex skills are packaged in the EvoDev Codex plugin payload.",
|
|
14675
14165
|
"Codex agents sync to user-level TOML files under ~/.codex/agents.",
|
|
@@ -14732,8 +14222,8 @@ function resolveCodexCapabilityArtifactPath(homeDir) {
|
|
|
14732
14222
|
async function writeCodexCapabilityVerificationArtifact(homeDir, artifact) {
|
|
14733
14223
|
validateCodexCapabilityVerificationArtifact(artifact);
|
|
14734
14224
|
const path = resolveCodexCapabilityArtifactPath(homeDir);
|
|
14735
|
-
await
|
|
14736
|
-
await
|
|
14225
|
+
await mkdir14(dirname15(path), { recursive: true });
|
|
14226
|
+
await writeFile13(path, `${JSON.stringify(artifact, null, 2)}
|
|
14737
14227
|
`, { encoding: "utf8", flag: "wx" });
|
|
14738
14228
|
return path;
|
|
14739
14229
|
}
|
|
@@ -14795,7 +14285,7 @@ async function runPluginConformance(plugin) {
|
|
|
14795
14285
|
}
|
|
14796
14286
|
return { ok: findings.length === 0, findings };
|
|
14797
14287
|
}
|
|
14798
|
-
function
|
|
14288
|
+
function sanitizeText4(value) {
|
|
14799
14289
|
return value.replace(/https?:\/\/\S+|\b(secret|token|password|private|internal|api[_-]?key)\b/gi, "[redacted]").slice(0, 300);
|
|
14800
14290
|
}
|
|
14801
14291
|
function assertNoSensitiveContent(value) {
|
|
@@ -14864,364 +14354,8 @@ function createPluginRegistry(plugins = []) {
|
|
|
14864
14354
|
function getEnabledPluginIds(settings) {
|
|
14865
14355
|
return Object.entries(settings.plugins).filter(([, pluginSettings]) => pluginSettings.enabled).map(([pluginId]) => pluginId).sort((left, right) => left.localeCompare(right));
|
|
14866
14356
|
}
|
|
14867
|
-
// packages/core/src/project/index.ts
|
|
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";
|
|
14870
|
-
var PROJECT_CONTEXT_ALLOWED_RELATIVE_PATHS = [
|
|
14871
|
-
".evodev/project.json",
|
|
14872
|
-
".evodev/profile.md",
|
|
14873
|
-
".evodev/index.json",
|
|
14874
|
-
".evodev/commands.json",
|
|
14875
|
-
".evodev/privacy.json",
|
|
14876
|
-
".evodev/decisions/README.md"
|
|
14877
|
-
];
|
|
14878
|
-
var EXCLUDED_DIRECTORY_NAMES = new Set([
|
|
14879
|
-
".git",
|
|
14880
|
-
".claude",
|
|
14881
|
-
".codex",
|
|
14882
|
-
"backups",
|
|
14883
|
-
"build",
|
|
14884
|
-
"coverage",
|
|
14885
|
-
"dist",
|
|
14886
|
-
"node_modules"
|
|
14887
|
-
]);
|
|
14888
|
-
var EXCLUDED_FILE_PREFIXES = [".env"];
|
|
14889
|
-
var EXCLUDED_FILE_PARTS = [
|
|
14890
|
-
"api-key",
|
|
14891
|
-
"api_key",
|
|
14892
|
-
"apikey",
|
|
14893
|
-
"credential",
|
|
14894
|
-
"credentials",
|
|
14895
|
-
"internal",
|
|
14896
|
-
"internal-link",
|
|
14897
|
-
"password",
|
|
14898
|
-
"passwords",
|
|
14899
|
-
"passwd",
|
|
14900
|
-
"private",
|
|
14901
|
-
"private-url",
|
|
14902
|
-
"secret",
|
|
14903
|
-
"token"
|
|
14904
|
-
];
|
|
14905
|
-
var EXCLUDED_FILE_EXTENSIONS = [".key", ".pem", ".p12", ".pfx"];
|
|
14906
|
-
var DOC_ENTRYPOINTS = new Set(["README.md"]);
|
|
14907
|
-
var DEFAULT_EXCLUDED_GLOBS = [
|
|
14908
|
-
".git/**",
|
|
14909
|
-
"node_modules/**",
|
|
14910
|
-
"dist/**",
|
|
14911
|
-
"build/**",
|
|
14912
|
-
"coverage/**",
|
|
14913
|
-
".env*",
|
|
14914
|
-
"**/*secret*",
|
|
14915
|
-
"**/*token*",
|
|
14916
|
-
"**/*password*",
|
|
14917
|
-
"**/*passwd*",
|
|
14918
|
-
"**/*api-key*",
|
|
14919
|
-
"**/*api_key*",
|
|
14920
|
-
"**/*apikey*",
|
|
14921
|
-
"**/*credential*",
|
|
14922
|
-
"**/*private*",
|
|
14923
|
-
"**/*internal*",
|
|
14924
|
-
"**/*.key",
|
|
14925
|
-
"**/*.pem",
|
|
14926
|
-
".claude/**",
|
|
14927
|
-
".codex/**",
|
|
14928
|
-
".evodev/backups/**"
|
|
14929
|
-
];
|
|
14930
|
-
var PROTECTED_PATTERNS = [
|
|
14931
|
-
"secret",
|
|
14932
|
-
"token",
|
|
14933
|
-
"password",
|
|
14934
|
-
"passwd",
|
|
14935
|
-
"api-key",
|
|
14936
|
-
"credential",
|
|
14937
|
-
"private",
|
|
14938
|
-
"private-url",
|
|
14939
|
-
"internal",
|
|
14940
|
-
"internal-link"
|
|
14941
|
-
];
|
|
14942
|
-
async function createProjectContextPlan(projectDir) {
|
|
14943
|
-
const projectDirStat = await stat12(projectDir);
|
|
14944
|
-
if (!projectDirStat.isDirectory()) {
|
|
14945
|
-
throw new Error(`Project dir is not a directory: ${projectDir}`);
|
|
14946
|
-
}
|
|
14947
|
-
const projectId = createProjectId(projectDir);
|
|
14948
|
-
const privacy = createProjectPrivacy();
|
|
14949
|
-
const files = await collectProjectFileMetadata(projectDir);
|
|
14950
|
-
const commands = await collectProjectCommands(projectDir);
|
|
14951
|
-
const docs = files.filter((file) => file.kind === "file" && DOC_ENTRYPOINTS.has(basename5(file.path))).map((file) => file.path).sort();
|
|
14952
|
-
const index = {
|
|
14953
|
-
version: 1,
|
|
14954
|
-
mode: "metadata-only",
|
|
14955
|
-
projectId,
|
|
14956
|
-
generatedAt: null,
|
|
14957
|
-
rootName: basename5(projectDir),
|
|
14958
|
-
files,
|
|
14959
|
-
docs: { entrypoints: docs },
|
|
14960
|
-
commands: {
|
|
14961
|
-
scripts: commands.scripts,
|
|
14962
|
-
testCommandCandidates: commands.scripts.filter((script) => script.commandClass === "test").map((script) => script.name)
|
|
14963
|
-
},
|
|
14964
|
-
exclusions: privacy.excludedGlobs,
|
|
14965
|
-
sourceContentIncluded: false
|
|
14966
|
-
};
|
|
14967
|
-
const planFiles = await Promise.all(PROJECT_CONTEXT_ALLOWED_RELATIVE_PATHS.map(async (relativePath) => {
|
|
14968
|
-
const absolutePath = join23(projectDir, relativePath);
|
|
14969
|
-
const exists = await pathExists9(absolutePath);
|
|
14970
|
-
return {
|
|
14971
|
-
relativePath,
|
|
14972
|
-
absolutePath,
|
|
14973
|
-
action: exists ? "error" : "create",
|
|
14974
|
-
reason: exists ? "Target already exists; I1 does not overwrite." : "Allowed project context file."
|
|
14975
|
-
};
|
|
14976
|
-
}));
|
|
14977
|
-
const errors2 = planFiles.filter((file) => file.action === "error").map((file) => `${file.relativePath}: ${file.reason}`);
|
|
14978
|
-
return {
|
|
14979
|
-
projectDir,
|
|
14980
|
-
projectId,
|
|
14981
|
-
files: planFiles,
|
|
14982
|
-
index,
|
|
14983
|
-
commands,
|
|
14984
|
-
privacy,
|
|
14985
|
-
warnings: commands.warnings,
|
|
14986
|
-
errors: errors2
|
|
14987
|
-
};
|
|
14988
|
-
}
|
|
14989
|
-
async function writeProjectContext(plan) {
|
|
14990
|
-
if (plan.errors.length > 0) {
|
|
14991
|
-
throw new Error(`Cannot write project context:
|
|
14992
|
-
${plan.errors.join(`
|
|
14993
|
-
`)}`);
|
|
14994
|
-
}
|
|
14995
|
-
const payloads = createProjectContextPayloads(plan);
|
|
14996
|
-
const writtenFiles = [];
|
|
14997
|
-
for (const relativePath of PROJECT_CONTEXT_ALLOWED_RELATIVE_PATHS) {
|
|
14998
|
-
const content = payloads[relativePath];
|
|
14999
|
-
const absolutePath = join23(plan.projectDir, relativePath);
|
|
15000
|
-
await mkdir16(join23(absolutePath, ".."), { recursive: true });
|
|
15001
|
-
await writeFile15(absolutePath, content, { encoding: "utf8", flag: "wx" });
|
|
15002
|
-
writtenFiles.push(relativePath);
|
|
15003
|
-
}
|
|
15004
|
-
return { writtenFiles };
|
|
15005
|
-
}
|
|
15006
|
-
function formatProjectContextPlan(plan, mode) {
|
|
15007
|
-
return [
|
|
15008
|
-
"EvoDev project init",
|
|
15009
|
-
"",
|
|
15010
|
-
`Mode: ${mode}`,
|
|
15011
|
-
`Project: ${plan.projectDir}`,
|
|
15012
|
-
`Project id: ${plan.projectId}`,
|
|
15013
|
-
"",
|
|
15014
|
-
"Plan:",
|
|
15015
|
-
...plan.files.map((file) => ` - ${file.action}: ${file.relativePath} (${file.reason})`),
|
|
15016
|
-
"",
|
|
15017
|
-
"Metadata-only index summary:",
|
|
15018
|
-
` - files: ${plan.index.files.filter((file) => file.kind === "file").length}`,
|
|
15019
|
-
` - directories: ${plan.index.files.filter((file) => file.kind === "directory").length}`,
|
|
15020
|
-
` - docs: ${plan.index.docs.entrypoints.length}`,
|
|
15021
|
-
` - package scripts: ${plan.commands.scripts.length}`,
|
|
15022
|
-
...plan.warnings.map((warning) => `Warning: ${warning}`),
|
|
15023
|
-
...plan.errors.map((error) => `Error: ${error}`)
|
|
15024
|
-
].join(`
|
|
15025
|
-
`);
|
|
15026
|
-
}
|
|
15027
|
-
function createProjectContextPayloads(plan) {
|
|
15028
|
-
const profile = {
|
|
15029
|
-
version: 1,
|
|
15030
|
-
projectId: plan.projectId,
|
|
15031
|
-
displayName: basename5(plan.projectDir),
|
|
15032
|
-
root: { pathPolicy: "local-only" },
|
|
15033
|
-
privacy: {
|
|
15034
|
-
classification: "local-private",
|
|
15035
|
-
metadataOnly: true,
|
|
15036
|
-
sourceContentIncluded: false,
|
|
15037
|
-
rawCommandsIncluded: false
|
|
15038
|
-
}
|
|
15039
|
-
};
|
|
15040
|
-
return {
|
|
15041
|
-
".evodev/project.json": `${JSON.stringify(profile, null, 2)}
|
|
15042
|
-
`,
|
|
15043
|
-
".evodev/profile.md": createProfileMarkdown(plan),
|
|
15044
|
-
".evodev/index.json": `${JSON.stringify(plan.index, null, 2)}
|
|
15045
|
-
`,
|
|
15046
|
-
".evodev/commands.json": `${JSON.stringify(plan.commands, null, 2)}
|
|
15047
|
-
`,
|
|
15048
|
-
".evodev/privacy.json": `${JSON.stringify(plan.privacy, null, 2)}
|
|
15049
|
-
`,
|
|
15050
|
-
".evodev/decisions/README.md": `# Project Decisions
|
|
15051
|
-
|
|
15052
|
-
Record project decisions here.
|
|
15053
|
-
`
|
|
15054
|
-
};
|
|
15055
|
-
}
|
|
15056
|
-
function createProfileMarkdown(plan) {
|
|
15057
|
-
return [
|
|
15058
|
-
`# ${basename5(plan.projectDir)} Project Context`,
|
|
15059
|
-
"",
|
|
15060
|
-
"This project context was generated as metadata-only local state.",
|
|
15061
|
-
"",
|
|
15062
|
-
`- Project id: ${plan.projectId}`,
|
|
15063
|
-
"- Source content included: false",
|
|
15064
|
-
"- Raw package script commands included: false",
|
|
15065
|
-
"- External upload: false",
|
|
15066
|
-
""
|
|
15067
|
-
].join(`
|
|
15068
|
-
`);
|
|
15069
|
-
}
|
|
15070
|
-
async function collectProjectFileMetadata(projectDir) {
|
|
15071
|
-
const files = [];
|
|
15072
|
-
async function visit(dir) {
|
|
15073
|
-
const entries = await readdir13(dir, { withFileTypes: true });
|
|
15074
|
-
for (const entry of entries) {
|
|
15075
|
-
const absolutePath = join23(dir, entry.name);
|
|
15076
|
-
const relativePath = relative8(projectDir, absolutePath).replaceAll("\\", "/");
|
|
15077
|
-
if (shouldExcludePath(relativePath, entry.isDirectory())) {
|
|
15078
|
-
continue;
|
|
15079
|
-
}
|
|
15080
|
-
if (entry.isDirectory()) {
|
|
15081
|
-
files.push({ path: relativePath, kind: "directory" });
|
|
15082
|
-
await visit(absolutePath);
|
|
15083
|
-
continue;
|
|
15084
|
-
}
|
|
15085
|
-
if (entry.isFile()) {
|
|
15086
|
-
const fileStat = await stat12(absolutePath);
|
|
15087
|
-
files.push({ path: relativePath, kind: "file", sizeBytes: fileStat.size });
|
|
15088
|
-
}
|
|
15089
|
-
}
|
|
15090
|
-
}
|
|
15091
|
-
await visit(projectDir);
|
|
15092
|
-
return files.sort((left, right) => left.path.localeCompare(right.path));
|
|
15093
|
-
}
|
|
15094
|
-
async function collectProjectCommands(projectDir) {
|
|
15095
|
-
const packageJsonPath = join23(projectDir, "package.json");
|
|
15096
|
-
const warnings = [];
|
|
15097
|
-
if (!await pathExists9(packageJsonPath)) {
|
|
15098
|
-
return { version: 1, metadataOnly: true, rawCommandsIncluded: false, scripts: [], warnings };
|
|
15099
|
-
}
|
|
15100
|
-
let parsed;
|
|
15101
|
-
try {
|
|
15102
|
-
parsed = JSON.parse(await readFile20(packageJsonPath, "utf8"));
|
|
15103
|
-
} catch (error) {
|
|
15104
|
-
warnings.push(`package.json scripts skipped: ${describeError4(error)}`);
|
|
15105
|
-
return { version: 1, metadataOnly: true, rawCommandsIncluded: false, scripts: [], warnings };
|
|
15106
|
-
}
|
|
15107
|
-
const scripts = isRecord13(parsed) && isRecord13(parsed.scripts) ? parsed.scripts : {};
|
|
15108
|
-
const summaries = [];
|
|
15109
|
-
for (const [name, value] of Object.entries(scripts).sort(([left], [right]) => left.localeCompare(right))) {
|
|
15110
|
-
if (typeof value !== "string") {
|
|
15111
|
-
continue;
|
|
15112
|
-
}
|
|
15113
|
-
const commandClass = classifyScript(name, value);
|
|
15114
|
-
const sensitiveReason = detectSensitiveScript(`${name} ${value}`);
|
|
15115
|
-
if (sensitiveReason !== null) {
|
|
15116
|
-
const safeName = `redacted-${commandClass}-script`;
|
|
15117
|
-
const warning = `Protected ${commandClass} script name/command omitted because it matched protected pattern: ${sensitiveReason}.`;
|
|
15118
|
-
warnings.push(warning);
|
|
15119
|
-
summaries.push({
|
|
15120
|
-
name: safeName,
|
|
15121
|
-
commandClass,
|
|
15122
|
-
summary: `Protected ${commandClass} script name/command omitted.`,
|
|
15123
|
-
rawCommandStored: false,
|
|
15124
|
-
redacted: true,
|
|
15125
|
-
warning
|
|
15126
|
-
});
|
|
15127
|
-
continue;
|
|
15128
|
-
}
|
|
15129
|
-
summaries.push({
|
|
15130
|
-
name,
|
|
15131
|
-
commandClass,
|
|
15132
|
-
summary: `Safe ${commandClass} script command summary only.`,
|
|
15133
|
-
rawCommandStored: false,
|
|
15134
|
-
redacted: false
|
|
15135
|
-
});
|
|
15136
|
-
}
|
|
15137
|
-
return {
|
|
15138
|
-
version: 1,
|
|
15139
|
-
metadataOnly: true,
|
|
15140
|
-
rawCommandsIncluded: false,
|
|
15141
|
-
scripts: summaries,
|
|
15142
|
-
warnings
|
|
15143
|
-
};
|
|
15144
|
-
}
|
|
15145
|
-
function classifyScript(name, command) {
|
|
15146
|
-
const text2 = `${name} ${command}`.toLowerCase();
|
|
15147
|
-
if (text2.includes("typecheck") || text2.includes("tsc"))
|
|
15148
|
-
return "typecheck";
|
|
15149
|
-
if (text2.includes("lint") || text2.includes("biome") || text2.includes("eslint"))
|
|
15150
|
-
return "lint";
|
|
15151
|
-
if (text2.includes("test"))
|
|
15152
|
-
return "test";
|
|
15153
|
-
if (text2.includes("build"))
|
|
15154
|
-
return "build";
|
|
15155
|
-
if (text2.includes("format") || text2.includes("prettier"))
|
|
15156
|
-
return "format";
|
|
15157
|
-
if (text2.includes("install"))
|
|
15158
|
-
return "install";
|
|
15159
|
-
if (text2.includes("release") || text2.includes("publish") || text2.includes("pack"))
|
|
15160
|
-
return "release";
|
|
15161
|
-
return "other";
|
|
15162
|
-
}
|
|
15163
|
-
function detectSensitiveScript(command) {
|
|
15164
|
-
const lower = command.toLowerCase();
|
|
15165
|
-
if (/(^|[^a-z0-9])(secret|token|password|passwd|api[_-]?key|apikey|credential|credentials|private|internal|private-url|internal-link)([^a-z0-9]|$)/.test(lower))
|
|
15166
|
-
return "protected-name-or-command";
|
|
15167
|
-
if (/https?:\/\//i.test(command))
|
|
15168
|
-
return "url";
|
|
15169
|
-
if (/\b[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,}\b/i.test(command))
|
|
15170
|
-
return "email-or-internal-id";
|
|
15171
|
-
return null;
|
|
15172
|
-
}
|
|
15173
|
-
function createProjectPrivacy() {
|
|
15174
|
-
return {
|
|
15175
|
-
version: 1,
|
|
15176
|
-
classification: "local-private",
|
|
15177
|
-
metadataOnly: true,
|
|
15178
|
-
sourceContentIndex: false,
|
|
15179
|
-
promptHistoryIndex: false,
|
|
15180
|
-
shellHistoryIndex: false,
|
|
15181
|
-
rawCommandOutputIndex: false,
|
|
15182
|
-
externalUpload: false,
|
|
15183
|
-
excludedGlobs: DEFAULT_EXCLUDED_GLOBS,
|
|
15184
|
-
protectedPatterns: PROTECTED_PATTERNS
|
|
15185
|
-
};
|
|
15186
|
-
}
|
|
15187
|
-
function createProjectId(projectDir) {
|
|
15188
|
-
return basename5(projectDir).toLowerCase().replace(/[^a-z0-9._-]+/g, "-").replace(/^-+|-+$/g, "") || "project";
|
|
15189
|
-
}
|
|
15190
|
-
function shouldExcludePath(relativePath, isDirectory) {
|
|
15191
|
-
const segments = relativePath.split("/");
|
|
15192
|
-
const name = segments.at(-1) ?? relativePath;
|
|
15193
|
-
const lowerName = name.toLowerCase();
|
|
15194
|
-
if (isDirectory && EXCLUDED_DIRECTORY_NAMES.has(name))
|
|
15195
|
-
return true;
|
|
15196
|
-
if (segments.includes(".evodev") && !relativePath.startsWith(".evodev/decisions"))
|
|
15197
|
-
return true;
|
|
15198
|
-
if (EXCLUDED_FILE_PREFIXES.some((prefix) => name.startsWith(prefix)))
|
|
15199
|
-
return true;
|
|
15200
|
-
if (EXCLUDED_FILE_PARTS.some((part) => lowerName.includes(part)))
|
|
15201
|
-
return true;
|
|
15202
|
-
if (EXCLUDED_FILE_EXTENSIONS.some((extension) => lowerName.endsWith(extension)))
|
|
15203
|
-
return true;
|
|
15204
|
-
return false;
|
|
15205
|
-
}
|
|
15206
|
-
async function pathExists9(path) {
|
|
15207
|
-
try {
|
|
15208
|
-
await stat12(path);
|
|
15209
|
-
return true;
|
|
15210
|
-
} catch (error) {
|
|
15211
|
-
if (error instanceof Error && "code" in error && error.code === "ENOENT") {
|
|
15212
|
-
return false;
|
|
15213
|
-
}
|
|
15214
|
-
throw error;
|
|
15215
|
-
}
|
|
15216
|
-
}
|
|
15217
|
-
function isRecord13(value) {
|
|
15218
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
15219
|
-
}
|
|
15220
|
-
function describeError4(error) {
|
|
15221
|
-
return error instanceof Error ? error.message : String(error);
|
|
15222
|
-
}
|
|
15223
14357
|
// packages/core/src/sync/orchestrator.ts
|
|
15224
|
-
import { join as
|
|
14358
|
+
import { join as join23 } from "node:path";
|
|
15225
14359
|
async function runSync(options) {
|
|
15226
14360
|
const store = createCoreConfigStore(options.homeDir);
|
|
15227
14361
|
const settings = await store.readSettings();
|
|
@@ -15364,8 +14498,8 @@ function filterAssetsForTarget(assets, targetPlugin) {
|
|
|
15364
14498
|
}
|
|
15365
14499
|
function resolveAssetScannerPaths(assetsRootDir) {
|
|
15366
14500
|
return {
|
|
15367
|
-
skillsDir:
|
|
15368
|
-
agentsDir:
|
|
14501
|
+
skillsDir: join23(assetsRootDir, "skills"),
|
|
14502
|
+
agentsDir: join23(assetsRootDir, "agents")
|
|
15369
14503
|
};
|
|
15370
14504
|
}
|
|
15371
14505
|
async function readRegistryOrDefault(store) {
|
|
@@ -15555,7 +14689,7 @@ async function handleTeamsMcpLine(line, options = {}) {
|
|
|
15555
14689
|
try {
|
|
15556
14690
|
return await handleTeamsMcpMessage(JSON.parse(line), options);
|
|
15557
14691
|
} catch (error) {
|
|
15558
|
-
return jsonRpcError(null, -32700, "Parse error",
|
|
14692
|
+
return jsonRpcError(null, -32700, "Parse error", describeError4(error));
|
|
15559
14693
|
}
|
|
15560
14694
|
}
|
|
15561
14695
|
async function handleTeamsMcpMessage(message, options = {}) {
|
|
@@ -15575,7 +14709,7 @@ async function handleTeamsMcpMessage(message, options = {}) {
|
|
|
15575
14709
|
}
|
|
15576
14710
|
return jsonRpcError(request.id, -32601, `Method not found: ${request.method}`);
|
|
15577
14711
|
} catch (error) {
|
|
15578
|
-
return jsonRpcError(request.id, -32603,
|
|
14712
|
+
return jsonRpcError(request.id, -32603, describeError4(error));
|
|
15579
14713
|
}
|
|
15580
14714
|
}
|
|
15581
14715
|
async function runTeamsMcpStdioServer(options = {}) {
|
|
@@ -15593,7 +14727,7 @@ async function runTeamsMcpStdioServer(options = {}) {
|
|
|
15593
14727
|
}
|
|
15594
14728
|
}
|
|
15595
14729
|
function initializeResult(request) {
|
|
15596
|
-
const params =
|
|
14730
|
+
const params = isRecord12(request.params) ? request.params : {};
|
|
15597
14731
|
const requestedVersion = typeof params.protocolVersion === "string" ? params.protocolVersion : PROTOCOL_VERSION;
|
|
15598
14732
|
const protocolVersion = SUPPORTED_PROTOCOL_VERSIONS.has(requestedVersion) ? requestedVersion : PROTOCOL_VERSION;
|
|
15599
14733
|
return {
|
|
@@ -15675,7 +14809,7 @@ function toolResult(structuredContent, isError = false) {
|
|
|
15675
14809
|
};
|
|
15676
14810
|
}
|
|
15677
14811
|
function parseRequest(message) {
|
|
15678
|
-
if (!
|
|
14812
|
+
if (!isRecord12(message))
|
|
15679
14813
|
throw new Error("Invalid JSON-RPC message; expected object.");
|
|
15680
14814
|
if (message.jsonrpc !== "2.0")
|
|
15681
14815
|
throw new Error("Invalid JSON-RPC version.");
|
|
@@ -15702,7 +14836,7 @@ function jsonRpcError(id, code, message, data) {
|
|
|
15702
14836
|
return { jsonrpc: "2.0", id, error: { code, message, data } };
|
|
15703
14837
|
}
|
|
15704
14838
|
function expectRecord5(value, path) {
|
|
15705
|
-
if (!
|
|
14839
|
+
if (!isRecord12(value))
|
|
15706
14840
|
throw new Error(`Invalid ${path}; expected object.`);
|
|
15707
14841
|
return value;
|
|
15708
14842
|
}
|
|
@@ -15715,28 +14849,28 @@ function expectString3(value, path) {
|
|
|
15715
14849
|
function optionalString7(value) {
|
|
15716
14850
|
return typeof value === "string" && value.length > 0 ? value : undefined;
|
|
15717
14851
|
}
|
|
15718
|
-
function
|
|
14852
|
+
function isRecord12(value) {
|
|
15719
14853
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
15720
14854
|
}
|
|
15721
|
-
function
|
|
14855
|
+
function describeError4(error) {
|
|
15722
14856
|
return error instanceof Error ? error.message : String(error);
|
|
15723
14857
|
}
|
|
15724
14858
|
// packages/core/src/workflow/index.ts
|
|
15725
|
-
import { readFile as
|
|
15726
|
-
import { join as
|
|
14859
|
+
import { readFile as readFile20, readdir as readdir14 } from "node:fs/promises";
|
|
14860
|
+
import { join as join24 } from "node:path";
|
|
15727
14861
|
async function scanWorkflowManifests(workflowsDir) {
|
|
15728
14862
|
const manifests = [];
|
|
15729
14863
|
const entries = await readdir14(workflowsDir, { withFileTypes: true });
|
|
15730
14864
|
for (const entry of entries) {
|
|
15731
14865
|
if (!entry.isDirectory())
|
|
15732
14866
|
continue;
|
|
15733
|
-
const manifestPath =
|
|
15734
|
-
manifests.push(parseWorkflowManifest(JSON.parse(await
|
|
14867
|
+
const manifestPath = join24(workflowsDir, entry.name, "WORKFLOW.json");
|
|
14868
|
+
manifests.push(parseWorkflowManifest(JSON.parse(await readFile20(manifestPath, "utf8"))));
|
|
15735
14869
|
}
|
|
15736
14870
|
return manifests.sort((left, right) => left.id.localeCompare(right.id));
|
|
15737
14871
|
}
|
|
15738
14872
|
function parseWorkflowManifest(value) {
|
|
15739
|
-
if (!
|
|
14873
|
+
if (!isRecord13(value))
|
|
15740
14874
|
throw new Error("Workflow manifest must be an object.");
|
|
15741
14875
|
const manifest = value;
|
|
15742
14876
|
if (typeof manifest.id !== "string" || typeof manifest.version !== "string") {
|
|
@@ -15751,20 +14885,12 @@ function parseWorkflowManifest(value) {
|
|
|
15751
14885
|
return manifest;
|
|
15752
14886
|
}
|
|
15753
14887
|
function planWorkflow(input) {
|
|
15754
|
-
const mode = input.contract?.route.mode ?? null;
|
|
15755
|
-
const warnings = [];
|
|
15756
|
-
const advisories = [];
|
|
15757
|
-
if (mode !== null && !input.workflow.modes.includes(mode)) {
|
|
15758
|
-
advisories.push(`Workflow ${input.workflow.id} does not list task mode ${mode}.`);
|
|
15759
|
-
}
|
|
15760
14888
|
return {
|
|
15761
14889
|
workflow: input.workflow,
|
|
15762
|
-
taskId: input.contract?.taskId,
|
|
15763
|
-
mode,
|
|
15764
14890
|
steps: input.workflow.steps.map((step) => ({ ...step, plannedOnly: true })),
|
|
15765
14891
|
requiredEvidence: input.workflow.requiredEvidence,
|
|
15766
|
-
warnings,
|
|
15767
|
-
advisories
|
|
14892
|
+
warnings: [],
|
|
14893
|
+
advisories: []
|
|
15768
14894
|
};
|
|
15769
14895
|
}
|
|
15770
14896
|
function formatWorkflowList(manifests) {
|
|
@@ -15780,8 +14906,6 @@ function formatWorkflowPlan(plan) {
|
|
|
15780
14906
|
"EvoDev workflow dry-run",
|
|
15781
14907
|
"",
|
|
15782
14908
|
`Workflow: ${plan.workflow.id}`,
|
|
15783
|
-
`Task: ${plan.taskId ?? "none"}`,
|
|
15784
|
-
`Mode: ${plan.mode ?? "not routed"}`,
|
|
15785
14909
|
"",
|
|
15786
14910
|
"Steps:",
|
|
15787
14911
|
...plan.steps.map((step) => ` - ${step.id}: ${step.name} [${step.actor}] evidence=${step.evidence.join(",") || "none"}`),
|
|
@@ -15793,13 +14917,11 @@ function formatWorkflowPlan(plan) {
|
|
|
15793
14917
|
].join(`
|
|
15794
14918
|
`);
|
|
15795
14919
|
}
|
|
15796
|
-
function
|
|
14920
|
+
function isRecord13(value) {
|
|
15797
14921
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
15798
14922
|
}
|
|
15799
14923
|
export {
|
|
15800
|
-
writeTaskContract,
|
|
15801
14924
|
writeSessionIndex,
|
|
15802
|
-
writeProjectContext,
|
|
15803
14925
|
writeJson2 as writeJson,
|
|
15804
14926
|
writeFailedOkfKnowledgePlanArtifact,
|
|
15805
14927
|
writeEvolutionRepoProposal,
|
|
@@ -15809,7 +14931,6 @@ export {
|
|
|
15809
14931
|
writeCodexCapabilityVerificationArtifact,
|
|
15810
14932
|
writeCodeAgentTraceRef,
|
|
15811
14933
|
withEvolutionReviewDecisionLock,
|
|
15812
|
-
verifyTaskContract,
|
|
15813
14934
|
validatePack,
|
|
15814
14935
|
validateOkfKnowledgePlanContract,
|
|
15815
14936
|
validateObservabilityEvent,
|
|
@@ -15848,7 +14969,6 @@ export {
|
|
|
15848
14969
|
runSync,
|
|
15849
14970
|
runPluginConformance,
|
|
15850
14971
|
runDaemonForeground,
|
|
15851
|
-
routeTaskContract,
|
|
15852
14972
|
revokeOkfKnowledgeConcept,
|
|
15853
14973
|
resumeTeamRun,
|
|
15854
14974
|
resumeFailedOkfKnowledgePlan,
|
|
@@ -15858,9 +14978,10 @@ export {
|
|
|
15858
14978
|
resolveTeamRole,
|
|
15859
14979
|
resolveTeamOverlay,
|
|
15860
14980
|
resolveTeamAgentReference,
|
|
15861
|
-
resolveTaskContractOutputPath,
|
|
15862
14981
|
resolveSessionMemoryPaths,
|
|
15863
14982
|
resolveSegmentEvolutionTriggerPath,
|
|
14983
|
+
resolveProjectWorkspaceFromCwd,
|
|
14984
|
+
resolveProjectRegistryPaths,
|
|
15864
14985
|
resolveProjectLogKey,
|
|
15865
14986
|
resolveOkfKnowledgePaths,
|
|
15866
14987
|
resolveObservabilityStorePaths,
|
|
@@ -15877,13 +14998,14 @@ export {
|
|
|
15877
14998
|
resolveCodeAgentTraceRefPaths,
|
|
15878
14999
|
renderRoleAgentDefinitionContext,
|
|
15879
15000
|
renderMainTeamOverlayContext,
|
|
15001
|
+
registerDiscoveredProject,
|
|
15880
15002
|
recordTeamAgentNativeSession,
|
|
15003
|
+
recordDiscoveredProject,
|
|
15881
15004
|
recordCodeAgentTraceRefFromHook,
|
|
15882
15005
|
reconcileTeamRun,
|
|
15883
15006
|
rebuildOkfKnowledgeIndexes,
|
|
15884
15007
|
readTeamAgentSummary,
|
|
15885
15008
|
readTeamAgentDefinition,
|
|
15886
|
-
readTaskContract,
|
|
15887
15009
|
readSessionState,
|
|
15888
15010
|
readSessionEvidenceSegment,
|
|
15889
15011
|
readSessionCursor,
|
|
@@ -15909,7 +15031,7 @@ export {
|
|
|
15909
15031
|
processEvolutionTriggers,
|
|
15910
15032
|
planWorkflow,
|
|
15911
15033
|
planPackInstallDryRun,
|
|
15912
|
-
|
|
15034
|
+
pathExists7 as pathExists,
|
|
15913
15035
|
parseWorkflowManifest,
|
|
15914
15036
|
parseTeamRoleDefinition,
|
|
15915
15037
|
parseTeamDefinitionMarkdown,
|
|
@@ -15936,14 +15058,17 @@ export {
|
|
|
15936
15058
|
mergeAcceptedEvosCasesIntoKnowledgeQuery,
|
|
15937
15059
|
markTeamMessagesDelivered,
|
|
15938
15060
|
markOkfKnowledgeConceptStale,
|
|
15061
|
+
markEvolutionRepoProposalApplied,
|
|
15939
15062
|
loadAgentContextDryRun,
|
|
15940
15063
|
loadAgentContext,
|
|
15941
15064
|
loadActiveOkfKnowledgeIndex,
|
|
15942
15065
|
listTeamRuns,
|
|
15943
15066
|
listTeamRoleBindings,
|
|
15944
15067
|
listTeamAgents,
|
|
15068
|
+
listSessionMemoryStates,
|
|
15945
15069
|
listSessionEvidenceSegments,
|
|
15946
15070
|
listSegmentEvolutionTriggers,
|
|
15071
|
+
listRegisteredProjects,
|
|
15947
15072
|
listOkfKnowledgeConcepts,
|
|
15948
15073
|
listObservabilityEvents,
|
|
15949
15074
|
listLearningReviewDecisions,
|
|
@@ -15952,6 +15077,7 @@ export {
|
|
|
15952
15077
|
listEvolutionKnowledgeReviewHistory,
|
|
15953
15078
|
listEvolutionKnowledgeRecords,
|
|
15954
15079
|
listEvolutionEvosCases,
|
|
15080
|
+
listDiscoveredProjects,
|
|
15955
15081
|
listCodeAgentTraceRefs,
|
|
15956
15082
|
lintOkfKnowledge,
|
|
15957
15083
|
lintLearningCandidates,
|
|
@@ -15973,10 +15099,8 @@ export {
|
|
|
15973
15099
|
getEnabledPluginIds,
|
|
15974
15100
|
formatWorkflowPlan,
|
|
15975
15101
|
formatWorkflowList,
|
|
15976
|
-
formatTaskContract,
|
|
15977
15102
|
formatScopedKnowledgePromptBlock,
|
|
15978
15103
|
formatRetentionDryRun,
|
|
15979
|
-
formatProjectContextPlan,
|
|
15980
15104
|
formatPackValidation,
|
|
15981
15105
|
formatPackInstallDryRun,
|
|
15982
15106
|
formatOkfKnowledgeQuery,
|
|
@@ -15998,7 +15122,6 @@ export {
|
|
|
15998
15122
|
formatCodexCapabilityVerificationArtifact,
|
|
15999
15123
|
formatAgentLoadedContextPromptBlock,
|
|
16000
15124
|
formatAgentContextDryRun,
|
|
16001
|
-
formatAgentComposeDryRun,
|
|
16002
15125
|
findCandidateConflictOrDuplicate,
|
|
16003
15126
|
exports_triggers as evolutionTriggers,
|
|
16004
15127
|
exports_review as evolutionReview,
|
|
@@ -16019,9 +15142,7 @@ export {
|
|
|
16019
15142
|
createTmuxRuntimeAdapter,
|
|
16020
15143
|
createTeamRunStore,
|
|
16021
15144
|
createTeamRoleRuntimeContext,
|
|
16022
|
-
createTaskContract,
|
|
16023
15145
|
createScopedKnowledgeContextPack,
|
|
16024
|
-
createProjectContextPlan,
|
|
16025
15146
|
createPluginRegistry,
|
|
16026
15147
|
createOkfKnowledgePlanFromDistillationBatch,
|
|
16027
15148
|
createObservabilityEvent,
|
|
@@ -16047,7 +15168,6 @@ export {
|
|
|
16047
15168
|
createCodexCapabilityVerificationArtifact,
|
|
16048
15169
|
createCodeAgentTraceRef,
|
|
16049
15170
|
createCliLogEntry,
|
|
16050
|
-
composeAgentDryRun,
|
|
16051
15171
|
cleanupDaemonState,
|
|
16052
15172
|
classifyCommandRisk,
|
|
16053
15173
|
checkProtectedZonePaths,
|
|
@@ -16070,9 +15190,9 @@ export {
|
|
|
16070
15190
|
TeamMessageBroker,
|
|
16071
15191
|
TEAM_INTERNAL_WAKE_SIGNAL,
|
|
16072
15192
|
SessionMemoryEvidenceError,
|
|
15193
|
+
ProjectRegistrationError,
|
|
16073
15194
|
PluginRegistryError,
|
|
16074
15195
|
PluginRegistry,
|
|
16075
|
-
PROJECT_CONTEXT_ALLOWED_RELATIVE_PATHS,
|
|
16076
15196
|
NodeTeamRuntimeCommandRunner,
|
|
16077
15197
|
EvoDevConfigError,
|
|
16078
15198
|
EvoDevAssetError,
|