@evo-dev/core 0.0.1-alpha.5 → 0.0.1-alpha.7
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 +6 -36
- package/dist/index.js +724 -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 +112 -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",
|
|
@@ -10124,6 +9751,12 @@ var CODEX_STOP_EVENTS_WITHOUT_ADDITIONAL_CONTEXT = new Set([
|
|
|
10124
9751
|
"Stop",
|
|
10125
9752
|
"SubagentStop"
|
|
10126
9753
|
]);
|
|
9754
|
+
var COMPLETION_EVENTS_WITHOUT_DEVELOPMENT_DIAGNOSTICS = new Set([
|
|
9755
|
+
"Stop",
|
|
9756
|
+
"SubagentStop",
|
|
9757
|
+
"TaskCompleted",
|
|
9758
|
+
"TeammateIdle"
|
|
9759
|
+
]);
|
|
10127
9760
|
function createDefaultHookSettings() {
|
|
10128
9761
|
return {
|
|
10129
9762
|
enabled: true,
|
|
@@ -10152,9 +9785,9 @@ function parseHookSettings(value) {
|
|
|
10152
9785
|
const defaults = createDefaultHookSettings();
|
|
10153
9786
|
if (value === undefined || value === null)
|
|
10154
9787
|
return defaults;
|
|
10155
|
-
if (!
|
|
9788
|
+
if (!isRecord8(value))
|
|
10156
9789
|
throw new Error("Invalid hooks settings; expected object.");
|
|
10157
|
-
const observability =
|
|
9790
|
+
const observability = isRecord8(value.observability) ? value.observability : undefined;
|
|
10158
9791
|
optionalBoolean3(observability?.metadataOnly, defaults.observability.metadataOnly, "hooks.observability.metadataOnly");
|
|
10159
9792
|
optionalBoolean3(observability?.rawPayloadStorage, defaults.observability.rawPayloadStorage, "hooks.observability.rawPayloadStorage");
|
|
10160
9793
|
return {
|
|
@@ -10288,8 +9921,7 @@ function resolveHookRuntimeSessionPaths(input) {
|
|
|
10288
9921
|
const sessionDir = join14(input.homeDir, ".evodev", "STATE", "hooks", "sessions", input.sessionKey);
|
|
10289
9922
|
return {
|
|
10290
9923
|
sessionDir,
|
|
10291
|
-
bindingPath: join14(sessionDir, "binding.json")
|
|
10292
|
-
contractPath: join14(sessionDir, "contract.json")
|
|
9924
|
+
bindingPath: join14(sessionDir, "binding.json")
|
|
10293
9925
|
};
|
|
10294
9926
|
}
|
|
10295
9927
|
async function handleHookRuntime(input) {
|
|
@@ -10305,7 +9937,13 @@ async function handleHookRuntime(input) {
|
|
|
10305
9937
|
summary: `Hook ${input.event.type} ignored because EvoDev hooks are disabled.`
|
|
10306
9938
|
};
|
|
10307
9939
|
}
|
|
9940
|
+
if (isActiveClaudeStopHook(input)) {
|
|
9941
|
+
return createRuntimeResult(input, null, {
|
|
9942
|
+
summary: `${input.event.type} re-entry allowed to complete without hook output.`
|
|
9943
|
+
});
|
|
9944
|
+
}
|
|
10308
9945
|
const diagnostics = await recordTeamNativeSessionFromHook(input);
|
|
9946
|
+
const projectDiagnostics = await recordDiscoveredProjectFromHook(input);
|
|
10309
9947
|
let result;
|
|
10310
9948
|
if (input.event.type === "SessionStart")
|
|
10311
9949
|
result = await handleSessionStart(input);
|
|
@@ -10333,7 +9971,8 @@ async function handleHookRuntime(input) {
|
|
|
10333
9971
|
const messageDiagnostics = await deliverPendingTeamMessagesFromHook(input, result);
|
|
10334
9972
|
const scopedContextDiagnostics = await injectScopedKnowledgeContextFromHook(input, result);
|
|
10335
9973
|
const teamStateDiagnostics = await recordTeamAgentHookStateFromHook(input);
|
|
10336
|
-
|
|
9974
|
+
const completed = appendRuntimeDiagnostics(appendRuntimeDiagnostics(appendRuntimeDiagnostics(appendRuntimeDiagnostics(appendRuntimeDiagnostics(appendRuntimeDiagnostics(appendRuntimeDiagnostics(result, diagnostics), projectDiagnostics), sessionMemoryDiagnostics), evolutionDiagnostics), messageDiagnostics), scopedContextDiagnostics), teamStateDiagnostics);
|
|
9975
|
+
return appendDevelopmentHookDiagnostics(input, completed);
|
|
10337
9976
|
}
|
|
10338
9977
|
function formatHookRuntimeOutput(result) {
|
|
10339
9978
|
return result.output === null ? "" : `${JSON.stringify(result.output)}
|
|
@@ -10342,6 +9981,29 @@ function formatHookRuntimeOutput(result) {
|
|
|
10342
9981
|
function isHookEventEnabled(settings, target, eventType) {
|
|
10343
9982
|
return settings.enabled === true && settings.targets[target]?.enabled === true && settings.targets[target]?.events[eventType] === true;
|
|
10344
9983
|
}
|
|
9984
|
+
async function recordDiscoveredProjectFromHook(input) {
|
|
9985
|
+
if (input.event.type !== "SessionStart" && input.event.type !== "UserPromptSubmit" && input.event.type !== "CwdChanged") {
|
|
9986
|
+
return { stateWrites: [], warnings: [] };
|
|
9987
|
+
}
|
|
9988
|
+
const cwd = optionalPayloadString(input.rawPayload.cwd);
|
|
9989
|
+
if (cwd === null)
|
|
9990
|
+
return { stateWrites: [], warnings: [] };
|
|
9991
|
+
try {
|
|
9992
|
+
const result = await recordDiscoveredProject({
|
|
9993
|
+
homeDir: input.homeDir,
|
|
9994
|
+
cwd,
|
|
9995
|
+
target: input.target,
|
|
9996
|
+
sessionKey: resolveTraceSessionKey(input.rawPayload),
|
|
9997
|
+
now: input.receivedAt === undefined || input.receivedAt === "dry-run" ? undefined : input.receivedAt
|
|
9998
|
+
});
|
|
9999
|
+
return result === null ? { stateWrites: [], warnings: [] } : { stateWrites: [result.path], warnings: [] };
|
|
10000
|
+
} catch {
|
|
10001
|
+
return {
|
|
10002
|
+
stateWrites: [],
|
|
10003
|
+
warnings: ["Local project discovery could not be updated at this hook safe point."]
|
|
10004
|
+
};
|
|
10005
|
+
}
|
|
10006
|
+
}
|
|
10345
10007
|
async function recordTeamNativeSessionFromHook(input) {
|
|
10346
10008
|
const environment = input.environment ?? {};
|
|
10347
10009
|
const runId = optionalPayloadString(environment.EVODEV_TEAM_RUN_ID);
|
|
@@ -10580,41 +10242,27 @@ async function handleSessionStart(input) {
|
|
|
10580
10242
|
});
|
|
10581
10243
|
}
|
|
10582
10244
|
async function handleUserPromptSubmit(input) {
|
|
10583
|
-
const classification = classifyUserPrompt(input.rawPayload.prompt ?? input.rawPayload.userPrompt);
|
|
10584
10245
|
const sessionKey = resolveHookSessionKey(input.rawPayload);
|
|
10585
10246
|
const paths2 = resolveHookRuntimeSessionPaths({ homeDir: input.homeDir, sessionKey });
|
|
10586
|
-
const contract = routeTaskContract(createHookTaskContract(input, classification));
|
|
10587
10247
|
const previousBinding = await readSessionBinding(input.homeDir, input.rawPayload);
|
|
10588
10248
|
const teamRuntimeContext = previousBinding?.teamRuntimeContextDeliveredAt === undefined || previousBinding.teamRuntimeContextDeliveredAt === null ? await createTeamRuntimeContextForUserPrompt(input) : null;
|
|
10589
|
-
const shouldShowDiagnostics = input.teamRuntimeDisplayMode === "development";
|
|
10590
10249
|
const teamRuntimeContextDeliveredAt = teamRuntimeContext !== null ? input.receivedAt ?? new Date().toISOString() : previousBinding?.teamRuntimeContextDeliveredAt ?? null;
|
|
10591
10250
|
const binding = {
|
|
10592
10251
|
version: 1,
|
|
10593
10252
|
target: input.target,
|
|
10594
10253
|
sessionKey,
|
|
10595
|
-
taskId: contract.taskId,
|
|
10596
|
-
contractPath: paths2.contractPath,
|
|
10597
10254
|
cwd: optionalPayloadString(input.rawPayload.cwd),
|
|
10598
|
-
route: contract.route,
|
|
10599
10255
|
teamRuntimeContextDeliveredAt,
|
|
10600
10256
|
updatedAt: input.receivedAt ?? new Date().toISOString()
|
|
10601
10257
|
};
|
|
10602
|
-
await writeTaskContract(paths2.contractPath, contract, { overwrite: true });
|
|
10603
10258
|
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
|
-
});
|
|
10259
|
+
let output = null;
|
|
10612
10260
|
if (teamRuntimeContext !== null) {
|
|
10613
10261
|
output = appendAdditionalContext(output, input.event.type, teamRuntimeContext);
|
|
10614
10262
|
}
|
|
10615
10263
|
return createRuntimeResult(input, output, {
|
|
10616
|
-
summary: "User prompt observed;
|
|
10617
|
-
stateWrites: [paths2.
|
|
10264
|
+
summary: "User prompt observed; session binding updated.",
|
|
10265
|
+
stateWrites: [paths2.bindingPath]
|
|
10618
10266
|
});
|
|
10619
10267
|
}
|
|
10620
10268
|
async function createTeamRuntimeContextForUserPrompt(input) {
|
|
@@ -10642,40 +10290,11 @@ async function handlePreToolUse(input) {
|
|
|
10642
10290
|
});
|
|
10643
10291
|
}
|
|
10644
10292
|
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
|
-
});
|
|
10293
|
+
return handleAdditionalContext(input, input.event.type);
|
|
10673
10294
|
}
|
|
10674
10295
|
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
10296
|
return createRuntimeResult(input, null, {
|
|
10678
|
-
summary: `${input.event.type} observed
|
|
10297
|
+
summary: `${input.event.type} observed as metadata-only hook context.`
|
|
10679
10298
|
});
|
|
10680
10299
|
}
|
|
10681
10300
|
async function handlePreCompact(input) {
|
|
@@ -10726,7 +10345,7 @@ function hookOutput(eventName, output) {
|
|
|
10726
10345
|
function appendAdditionalContext(output, eventName, context) {
|
|
10727
10346
|
if (output === null)
|
|
10728
10347
|
return hookOutput(eventName, { additionalContext: context });
|
|
10729
|
-
const hookSpecificOutput =
|
|
10348
|
+
const hookSpecificOutput = isRecord8(output.hookSpecificOutput) ? output.hookSpecificOutput : {};
|
|
10730
10349
|
const previous = typeof hookSpecificOutput.additionalContext === "string" ? hookSpecificOutput.additionalContext : "";
|
|
10731
10350
|
return {
|
|
10732
10351
|
...output,
|
|
@@ -10739,6 +10358,34 @@ ${context}`
|
|
|
10739
10358
|
}
|
|
10740
10359
|
};
|
|
10741
10360
|
}
|
|
10361
|
+
function appendDevelopmentHookDiagnostics(input, result) {
|
|
10362
|
+
if (input.teamRuntimeDisplayMode !== "development" || COMPLETION_EVENTS_WITHOUT_DEVELOPMENT_DIAGNOSTICS.has(input.event.type) || !canDeliverTeamMessagesFromHook(input.target, input.event.type)) {
|
|
10363
|
+
return result;
|
|
10364
|
+
}
|
|
10365
|
+
return {
|
|
10366
|
+
...result,
|
|
10367
|
+
output: appendAdditionalContext(result.output, input.event.type, formatDevelopmentHookDiagnostics(input))
|
|
10368
|
+
};
|
|
10369
|
+
}
|
|
10370
|
+
function isActiveClaudeStopHook(input) {
|
|
10371
|
+
return input.target === "claude" && (input.event.type === "Stop" || input.event.type === "SubagentStop") && input.rawPayload.stop_hook_active === true;
|
|
10372
|
+
}
|
|
10373
|
+
function formatDevelopmentHookDiagnostics(input) {
|
|
10374
|
+
const metadata = Object.entries(input.event.payload.metadata);
|
|
10375
|
+
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);
|
|
10376
|
+
return [
|
|
10377
|
+
"EvoDev hook development diagnostics",
|
|
10378
|
+
`Target: ${input.target}`,
|
|
10379
|
+
`Event: ${input.event.type}`,
|
|
10380
|
+
`Summary: ${input.event.payload.summary}`,
|
|
10381
|
+
`Input fields: ${inputFields.join(", ") || "none"}`,
|
|
10382
|
+
"Normalized metadata:",
|
|
10383
|
+
...metadata.length === 0 ? ["- none"] : metadata.map(([key, value]) => `- ${key}: ${formatMetadataValue(value)}`),
|
|
10384
|
+
`Redactions: ${input.event.payload.redactions.join(", ") || "none"}`,
|
|
10385
|
+
"Raw payload included: false"
|
|
10386
|
+
].join(`
|
|
10387
|
+
`);
|
|
10388
|
+
}
|
|
10742
10389
|
function formatTeamInboxContext(messages) {
|
|
10743
10390
|
const blocks = messages.map((message) => [
|
|
10744
10391
|
"[EvoDev team message]",
|
|
@@ -10762,101 +10409,6 @@ function truncateTeamMessageBody(value) {
|
|
|
10762
10409
|
return value;
|
|
10763
10410
|
return `${value.slice(0, 4000)}...[truncated:${value.length - 4000}]`;
|
|
10764
10411
|
}
|
|
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
10412
|
async function readSessionBinding(homeDir, payload) {
|
|
10861
10413
|
const sessionKey = resolveHookSessionKey(payload);
|
|
10862
10414
|
const paths2 = resolveHookRuntimeSessionPaths({ homeDir, sessionKey });
|
|
@@ -10875,8 +10427,8 @@ function resolveHookSessionKey(payload) {
|
|
|
10875
10427
|
return `session-${sha256Short(source)}`;
|
|
10876
10428
|
}
|
|
10877
10429
|
async function writeJsonFile2(path, value) {
|
|
10878
|
-
await
|
|
10879
|
-
await
|
|
10430
|
+
await mkdir8(dirname9(path), { recursive: true });
|
|
10431
|
+
await writeFile7(path, `${JSON.stringify(value, null, 2)}
|
|
10880
10432
|
`, "utf8");
|
|
10881
10433
|
}
|
|
10882
10434
|
function optionalPayloadString(value) {
|
|
@@ -10885,9 +10437,6 @@ function optionalPayloadString(value) {
|
|
|
10885
10437
|
function safeDiagnosticId(value) {
|
|
10886
10438
|
return value.replace(/[^A-Za-z0-9._-]/g, "-").slice(0, 120) || "unknown";
|
|
10887
10439
|
}
|
|
10888
|
-
function optionalPayloadNumber(value) {
|
|
10889
|
-
return typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
10890
|
-
}
|
|
10891
10440
|
function isNotFoundError4(error) {
|
|
10892
10441
|
return error instanceof Error && "code" in error && error.code === "ENOENT";
|
|
10893
10442
|
}
|
|
@@ -10901,9 +10450,9 @@ function normalizeHookEventType(type, warnings) {
|
|
|
10901
10450
|
throw new Error(`Unsupported hook event type: ${type}`);
|
|
10902
10451
|
}
|
|
10903
10452
|
function parseHookTargetSettings(value, defaults, target) {
|
|
10904
|
-
const targets =
|
|
10905
|
-
const targetSettings =
|
|
10906
|
-
const events =
|
|
10453
|
+
const targets = isRecord8(value) ? value : {};
|
|
10454
|
+
const targetSettings = isRecord8(targets[target]) ? targets[target] : {};
|
|
10455
|
+
const events = isRecord8(targetSettings.events) ? targetSettings.events : {};
|
|
10907
10456
|
const parsedEvents = { ...defaults.events };
|
|
10908
10457
|
for (const eventType of CANONICAL_HOOK_EVENT_TYPES) {
|
|
10909
10458
|
parsedEvents[eventType] = optionalBoolean3(events[eventType], defaults.events[eventType], `hooks.targets.${target}.events.${eventType}`);
|
|
@@ -10915,7 +10464,7 @@ function parseHookTargetSettings(value, defaults, target) {
|
|
|
10915
10464
|
}
|
|
10916
10465
|
function extractMetadata(type, payload, redactions) {
|
|
10917
10466
|
const metadata = {};
|
|
10918
|
-
const toolInput =
|
|
10467
|
+
const toolInput = isRecord8(payload.tool_input) ? payload.tool_input : isRecord8(payload.toolInput) ? payload.toolInput : {};
|
|
10919
10468
|
const toolName = optionalSanitizedString(payload.tool_name ?? payload.toolName, redactions);
|
|
10920
10469
|
if (toolName !== null)
|
|
10921
10470
|
metadata.toolName = toolName;
|
|
@@ -10962,7 +10511,7 @@ function summarizeEvent(type, metadata) {
|
|
|
10962
10511
|
function optionalSanitizedString(value, redactions, options = {}) {
|
|
10963
10512
|
if (typeof value !== "string" || value.length === 0)
|
|
10964
10513
|
return null;
|
|
10965
|
-
if (
|
|
10514
|
+
if (SENSITIVE_TEXT_PATTERN3.test(value) || SOURCE_LIKE_PATTERN.test(value)) {
|
|
10966
10515
|
redactions.push(options.classifyOnly ? "sensitive-command" : "sensitive-text");
|
|
10967
10516
|
return options.classifyOnly ? value : "[redacted]";
|
|
10968
10517
|
}
|
|
@@ -10993,7 +10542,7 @@ function optionalNumber(value) {
|
|
|
10993
10542
|
function formatMetadataValue(value) {
|
|
10994
10543
|
return Array.isArray(value) ? value.join(",") : String(value);
|
|
10995
10544
|
}
|
|
10996
|
-
function
|
|
10545
|
+
function isRecord8(value) {
|
|
10997
10546
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
10998
10547
|
}
|
|
10999
10548
|
|
|
@@ -11295,20 +10844,20 @@ function expectNonNegativeInteger(value, path) {
|
|
|
11295
10844
|
return value;
|
|
11296
10845
|
}
|
|
11297
10846
|
// packages/core/src/config/store.ts
|
|
11298
|
-
import { mkdir as
|
|
10847
|
+
import { mkdir as mkdir9, readFile as readFile13, writeFile as writeFile8 } from "node:fs/promises";
|
|
11299
10848
|
import { dirname as dirname10 } from "node:path";
|
|
11300
10849
|
function createCoreConfigStore(homeDir) {
|
|
11301
10850
|
const paths2 = resolveEvoDevPaths(homeDir);
|
|
11302
10851
|
return {
|
|
11303
10852
|
paths: paths2,
|
|
11304
10853
|
async ensureBaseDirs() {
|
|
11305
|
-
await
|
|
11306
|
-
await
|
|
11307
|
-
await
|
|
11308
|
-
await
|
|
11309
|
-
await
|
|
11310
|
-
await
|
|
11311
|
-
await
|
|
10854
|
+
await mkdir9(paths2.stateDir, { recursive: true });
|
|
10855
|
+
await mkdir9(paths2.logsDir, { recursive: true });
|
|
10856
|
+
await mkdir9(paths2.knowledgeDir, { recursive: true });
|
|
10857
|
+
await mkdir9(paths2.evosCasesDir, { recursive: true });
|
|
10858
|
+
await mkdir9(paths2.roleAgentsDir, { recursive: true });
|
|
10859
|
+
await mkdir9(paths2.teamsDir, { recursive: true });
|
|
10860
|
+
await mkdir9(paths2.runsDir, { recursive: true });
|
|
11312
10861
|
},
|
|
11313
10862
|
async ensureKnowledgeBase() {
|
|
11314
10863
|
await ensureKnowledgeBaseFiles(paths2);
|
|
@@ -11356,8 +10905,8 @@ async function initializeCoreConfig(homeDir) {
|
|
|
11356
10905
|
return store;
|
|
11357
10906
|
}
|
|
11358
10907
|
async function ensureKnowledgeBaseFiles(paths2) {
|
|
11359
|
-
await
|
|
11360
|
-
await
|
|
10908
|
+
await mkdir9(paths2.knowledgeDir, { recursive: true });
|
|
10909
|
+
await mkdir9(paths2.evosCasesDir, { recursive: true });
|
|
11361
10910
|
await ensureOkfKnowledgeBase(paths2.homeDir);
|
|
11362
10911
|
await writeTextIfMissing2(`${paths2.knowledgeDir}/README.md`, [
|
|
11363
10912
|
"# EvoDev Knowledge",
|
|
@@ -11426,7 +10975,7 @@ async function ensureKnowledgeBaseFiles(paths2) {
|
|
|
11426
10975
|
teams: []
|
|
11427
10976
|
});
|
|
11428
10977
|
}
|
|
11429
|
-
async function readJsonFile2(filePath,
|
|
10978
|
+
async function readJsonFile2(filePath, parse2) {
|
|
11430
10979
|
let raw;
|
|
11431
10980
|
try {
|
|
11432
10981
|
raw = await readFile13(filePath, "utf8");
|
|
@@ -11440,7 +10989,7 @@ async function readJsonFile2(filePath, parse) {
|
|
|
11440
10989
|
throw new EvoDevConfigError(`Invalid JSON (${describeFileError2(error)})`, filePath);
|
|
11441
10990
|
}
|
|
11442
10991
|
try {
|
|
11443
|
-
return
|
|
10992
|
+
return parse2(json);
|
|
11444
10993
|
} catch (error) {
|
|
11445
10994
|
if (error instanceof EvoDevConfigError) {
|
|
11446
10995
|
throw new EvoDevConfigError(error.message, filePath);
|
|
@@ -11448,9 +10997,9 @@ async function readJsonFile2(filePath, parse) {
|
|
|
11448
10997
|
throw error;
|
|
11449
10998
|
}
|
|
11450
10999
|
}
|
|
11451
|
-
async function readJsonFileOrDefault(filePath,
|
|
11000
|
+
async function readJsonFileOrDefault(filePath, parse2, fallback) {
|
|
11452
11001
|
try {
|
|
11453
|
-
return await readJsonFile2(filePath,
|
|
11002
|
+
return await readJsonFile2(filePath, parse2);
|
|
11454
11003
|
} catch (error) {
|
|
11455
11004
|
if (error instanceof EvoDevConfigError && error.message.includes("ENOENT")) {
|
|
11456
11005
|
return fallback;
|
|
@@ -11486,7 +11035,7 @@ async function writeIndexIfMissingOrMigrate(filePath, kind, defaults) {
|
|
|
11486
11035
|
} catch (error) {
|
|
11487
11036
|
throw new EvoDevConfigError(`Invalid bootstrap index JSON (${describeFileError2(error)})`, filePath);
|
|
11488
11037
|
}
|
|
11489
|
-
if (!
|
|
11038
|
+
if (!isRecord9(existing) || existing.kind !== kind)
|
|
11490
11039
|
return;
|
|
11491
11040
|
const migrated = { ...defaults, ...existing };
|
|
11492
11041
|
if (Object.keys(defaults).every((key) => (key in existing)))
|
|
@@ -11498,16 +11047,16 @@ async function writeTextIfMissing2(filePath, value) {
|
|
|
11498
11047
|
await readFile13(filePath, "utf8");
|
|
11499
11048
|
} catch (error) {
|
|
11500
11049
|
if (isNodeError2(error) && error.code === "ENOENT") {
|
|
11501
|
-
await
|
|
11502
|
-
await
|
|
11050
|
+
await mkdir9(dirname10(filePath), { recursive: true });
|
|
11051
|
+
await writeFile8(filePath, value, "utf8");
|
|
11503
11052
|
return;
|
|
11504
11053
|
}
|
|
11505
11054
|
throw new EvoDevConfigError(`Cannot inspect config file (${describeFileError2(error)})`, filePath);
|
|
11506
11055
|
}
|
|
11507
11056
|
}
|
|
11508
11057
|
async function writeJsonFile3(filePath, value) {
|
|
11509
|
-
await
|
|
11510
|
-
await
|
|
11058
|
+
await mkdir9(dirname10(filePath), { recursive: true });
|
|
11059
|
+
await writeFile8(filePath, `${JSON.stringify(value, null, 2)}
|
|
11511
11060
|
`, "utf8");
|
|
11512
11061
|
}
|
|
11513
11062
|
function describeFileError2(error) {
|
|
@@ -11519,12 +11068,12 @@ function describeFileError2(error) {
|
|
|
11519
11068
|
function isNodeError2(error) {
|
|
11520
11069
|
return error instanceof Error && "code" in error;
|
|
11521
11070
|
}
|
|
11522
|
-
function
|
|
11071
|
+
function isRecord9(value) {
|
|
11523
11072
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
11524
11073
|
}
|
|
11525
11074
|
// packages/core/src/daemon/index.ts
|
|
11526
11075
|
import { randomBytes } from "node:crypto";
|
|
11527
|
-
import { mkdir as
|
|
11076
|
+
import { mkdir as mkdir12, readFile as readFile16, readdir as readdir12, rm as rm4, stat as stat9, writeFile as writeFile11 } from "node:fs/promises";
|
|
11528
11077
|
import { createServer } from "node:http";
|
|
11529
11078
|
import { dirname as dirname13, join as join19 } from "node:path";
|
|
11530
11079
|
|
|
@@ -11676,7 +11225,7 @@ __export(exports_processor, {
|
|
|
11676
11225
|
});
|
|
11677
11226
|
|
|
11678
11227
|
// packages/core/src/evolution/evidence/analysis.ts
|
|
11679
|
-
import { readFile as readFile14, readdir as
|
|
11228
|
+
import { readFile as readFile14, readdir as readdir10 } from "node:fs/promises";
|
|
11680
11229
|
import { basename as basename3, join as join15 } from "node:path";
|
|
11681
11230
|
async function analyzeEvolutionRun(input) {
|
|
11682
11231
|
const projectKey = resolveEvolutionProjectKey(input);
|
|
@@ -11689,8 +11238,8 @@ async function analyzeEvolutionRun(input) {
|
|
|
11689
11238
|
const logsDir = resolveEvoDevPaths(input.homeDir).logsDir;
|
|
11690
11239
|
const projectRunAgentsDir = join15(logsDir, "teams", projectKey, runId, "agents");
|
|
11691
11240
|
const legacyProjectRunAgentsDir = join15(logsDir, projectKey, runId, "agents");
|
|
11692
|
-
const agentDirs = await pathExists(projectRunAgentsDir) ? await
|
|
11693
|
-
const legacyAgentDirs = await pathExists(legacyProjectRunAgentsDir) ? await
|
|
11241
|
+
const agentDirs = await pathExists(projectRunAgentsDir) ? await readdir10(projectRunAgentsDir, { withFileTypes: true }) : [];
|
|
11242
|
+
const legacyAgentDirs = await pathExists(legacyProjectRunAgentsDir) ? await readdir10(legacyProjectRunAgentsDir, { withFileTypes: true }) : [];
|
|
11694
11243
|
if (agentDirs.length === 0 && legacyAgentDirs.length === 0) {
|
|
11695
11244
|
warnings.push(`No agent execution event directory found: ${displayPath(input.homeDir, projectRunAgentsDir)}`);
|
|
11696
11245
|
} else {
|
|
@@ -12376,7 +11925,7 @@ function createProvenance(evidenceWindow, createdAt, sourceRefs) {
|
|
|
12376
11925
|
};
|
|
12377
11926
|
}
|
|
12378
11927
|
// packages/core/src/evolution/processor/process.ts
|
|
12379
|
-
import { mkdir as
|
|
11928
|
+
import { mkdir as mkdir10, rm as rm3, stat as stat7, writeFile as writeFile9 } from "node:fs/promises";
|
|
12380
11929
|
import { dirname as dirname11, join as join17 } from "node:path";
|
|
12381
11930
|
var PROCESS_LOCK_STALE_MS2 = 5 * 60 * 1000;
|
|
12382
11931
|
async function processEvolutionTriggers(input) {
|
|
@@ -12537,9 +12086,9 @@ async function processEvolutionTriggers(input) {
|
|
|
12537
12086
|
async function acquireEvolutionProcessLock(homeDir, now) {
|
|
12538
12087
|
const paths2 = resolveEvoDevPaths(homeDir);
|
|
12539
12088
|
const lockPath = join17(paths2.stateDir, "evolution", ".process.lock");
|
|
12540
|
-
await
|
|
12089
|
+
await mkdir10(dirname11(lockPath), { recursive: true });
|
|
12541
12090
|
try {
|
|
12542
|
-
await
|
|
12091
|
+
await writeFile9(lockPath, `${JSON.stringify({
|
|
12543
12092
|
schemaVersion: 1,
|
|
12544
12093
|
kind: "evolution-process-lock",
|
|
12545
12094
|
createdAt: now,
|
|
@@ -12562,7 +12111,7 @@ async function releaseEvolutionProcessLock(lock) {
|
|
|
12562
12111
|
await rm3(lock.path, { force: true });
|
|
12563
12112
|
}
|
|
12564
12113
|
// packages/core/src/observability/index.ts
|
|
12565
|
-
import { mkdir as
|
|
12114
|
+
import { mkdir as mkdir11, readFile as readFile15, readdir as readdir11, stat as stat8, writeFile as writeFile10 } from "node:fs/promises";
|
|
12566
12115
|
import { dirname as dirname12, join as join18 } from "node:path";
|
|
12567
12116
|
var EVENT_TYPE_TO_DIR = {
|
|
12568
12117
|
"verification.completed": "verification",
|
|
@@ -12570,7 +12119,7 @@ var EVENT_TYPE_TO_DIR = {
|
|
|
12570
12119
|
"test-run.completed": "tests",
|
|
12571
12120
|
"review.finding": "reviews"
|
|
12572
12121
|
};
|
|
12573
|
-
var
|
|
12122
|
+
var FORBIDDEN_RAW_KEYS2 = new Set([
|
|
12574
12123
|
"commandoutput",
|
|
12575
12124
|
"memorybodies",
|
|
12576
12125
|
"memorybody",
|
|
@@ -12592,11 +12141,11 @@ var FORBIDDEN_RAW_KEYS3 = new Set([
|
|
|
12592
12141
|
"transcriptbody",
|
|
12593
12142
|
"transcripttext"
|
|
12594
12143
|
]);
|
|
12595
|
-
var
|
|
12144
|
+
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
12145
|
function createObservabilityEvent(input) {
|
|
12597
12146
|
const event = {
|
|
12598
12147
|
version: 1,
|
|
12599
|
-
eventId:
|
|
12148
|
+
eventId: sanitizeId2(input.eventId),
|
|
12600
12149
|
type: input.type,
|
|
12601
12150
|
timestamp: input.timestamp ?? new Date().toISOString(),
|
|
12602
12151
|
scope: input.scope,
|
|
@@ -12608,7 +12157,7 @@ function createObservabilityEvent(input) {
|
|
|
12608
12157
|
sourceContentStored: false,
|
|
12609
12158
|
promptStored: false
|
|
12610
12159
|
},
|
|
12611
|
-
summary:
|
|
12160
|
+
summary: sanitizeText2(input.summary),
|
|
12612
12161
|
data: sanitizeData(input.data ?? {})
|
|
12613
12162
|
};
|
|
12614
12163
|
validateObservabilityEvent(event);
|
|
@@ -12631,8 +12180,8 @@ function resolveObservabilityStorePaths(homeDir, type) {
|
|
|
12631
12180
|
async function appendObservabilityEvent(homeDir, event) {
|
|
12632
12181
|
validateObservabilityEvent(event);
|
|
12633
12182
|
const paths2 = resolveObservabilityStorePaths(homeDir, event.type);
|
|
12634
|
-
await
|
|
12635
|
-
await
|
|
12183
|
+
await mkdir11(dirname12(paths2.eventsPath), { recursive: true });
|
|
12184
|
+
await writeFile10(paths2.eventsPath, `${JSON.stringify(event)}
|
|
12636
12185
|
`, { encoding: "utf8", flag: "a" });
|
|
12637
12186
|
return paths2.eventsPath;
|
|
12638
12187
|
}
|
|
@@ -12641,7 +12190,7 @@ async function listObservabilityEvents(homeDir, type) {
|
|
|
12641
12190
|
const events = [];
|
|
12642
12191
|
for (const eventType of eventTypes) {
|
|
12643
12192
|
const path = resolveObservabilityStorePaths(homeDir, eventType).eventsPath;
|
|
12644
|
-
if (!await
|
|
12193
|
+
if (!await pathExists5(path))
|
|
12645
12194
|
continue;
|
|
12646
12195
|
const lines = (await readFile15(path, "utf8")).split(`
|
|
12647
12196
|
`).filter(Boolean);
|
|
@@ -12656,7 +12205,7 @@ async function listObservabilityEvents(homeDir, type) {
|
|
|
12656
12205
|
async function dryRunObservabilityRetentionCleanup(homeDir) {
|
|
12657
12206
|
const root = join18(homeDir, ".evodev", "OBSERVABILITY");
|
|
12658
12207
|
const candidates = [];
|
|
12659
|
-
if (!await
|
|
12208
|
+
if (!await pathExists5(root))
|
|
12660
12209
|
return { candidates, totalBytes: 0 };
|
|
12661
12210
|
for (const file of await collectJsonlFiles(root)) {
|
|
12662
12211
|
const fileStat = await stat8(file);
|
|
@@ -12689,13 +12238,13 @@ function sanitizeData(data) {
|
|
|
12689
12238
|
const sanitized = {};
|
|
12690
12239
|
for (const [key, value] of Object.entries(data)) {
|
|
12691
12240
|
const normalizedKey = normalizeKey(key);
|
|
12692
|
-
if (
|
|
12241
|
+
if (FORBIDDEN_RAW_KEYS2.has(normalizedKey)) {
|
|
12693
12242
|
throw new Error(`Observability data contains forbidden raw field: ${key}`);
|
|
12694
12243
|
}
|
|
12695
12244
|
if (typeof value === "string")
|
|
12696
|
-
sanitized[key] =
|
|
12245
|
+
sanitized[key] = sanitizeText2(value);
|
|
12697
12246
|
else if (Array.isArray(value))
|
|
12698
|
-
sanitized[key] = value.map(
|
|
12247
|
+
sanitized[key] = value.map(sanitizeText2);
|
|
12699
12248
|
else
|
|
12700
12249
|
sanitized[key] = value;
|
|
12701
12250
|
}
|
|
@@ -12705,7 +12254,7 @@ function assertNoForbiddenContent(value) {
|
|
|
12705
12254
|
if (typeof value === "string") {
|
|
12706
12255
|
if (value === "local-private")
|
|
12707
12256
|
return;
|
|
12708
|
-
if (
|
|
12257
|
+
if (SENSITIVE_TEXT_PATTERN4.test(value))
|
|
12709
12258
|
throw new Error("Observability event contains sensitive content.");
|
|
12710
12259
|
return;
|
|
12711
12260
|
}
|
|
@@ -12718,14 +12267,14 @@ function assertNoForbiddenContent(value) {
|
|
|
12718
12267
|
return;
|
|
12719
12268
|
for (const [key, child] of Object.entries(value)) {
|
|
12720
12269
|
const normalizedKey = normalizeKey(key);
|
|
12721
|
-
if (
|
|
12270
|
+
if (FORBIDDEN_RAW_KEYS2.has(normalizedKey)) {
|
|
12722
12271
|
throw new Error(`Observability event contains forbidden raw field: ${key}`);
|
|
12723
12272
|
}
|
|
12724
12273
|
assertNoForbiddenContent(child);
|
|
12725
12274
|
}
|
|
12726
12275
|
}
|
|
12727
12276
|
async function collectJsonlFiles(root) {
|
|
12728
|
-
const entries = await
|
|
12277
|
+
const entries = await readdir11(root, { withFileTypes: true });
|
|
12729
12278
|
const files = [];
|
|
12730
12279
|
for (const entry of entries) {
|
|
12731
12280
|
const path = join18(root, entry.name);
|
|
@@ -12736,7 +12285,7 @@ async function collectJsonlFiles(root) {
|
|
|
12736
12285
|
}
|
|
12737
12286
|
return files.sort();
|
|
12738
12287
|
}
|
|
12739
|
-
async function
|
|
12288
|
+
async function pathExists5(path) {
|
|
12740
12289
|
try {
|
|
12741
12290
|
await stat8(path);
|
|
12742
12291
|
return true;
|
|
@@ -12746,10 +12295,10 @@ async function pathExists6(path) {
|
|
|
12746
12295
|
throw error;
|
|
12747
12296
|
}
|
|
12748
12297
|
}
|
|
12749
|
-
function
|
|
12750
|
-
return value.replace(
|
|
12298
|
+
function sanitizeText2(value) {
|
|
12299
|
+
return value.replace(SENSITIVE_TEXT_PATTERN4, "[redacted]").slice(0, 500);
|
|
12751
12300
|
}
|
|
12752
|
-
function
|
|
12301
|
+
function sanitizeId2(value) {
|
|
12753
12302
|
return value.replace(/[^a-zA-Z0-9._-]/g, "-").slice(0, 80) || "event";
|
|
12754
12303
|
}
|
|
12755
12304
|
function normalizeKey(key) {
|
|
@@ -12758,7 +12307,7 @@ function normalizeKey(key) {
|
|
|
12758
12307
|
|
|
12759
12308
|
// packages/core/src/daemon/index.ts
|
|
12760
12309
|
var DEFAULT_PORT = 37645;
|
|
12761
|
-
var
|
|
12310
|
+
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
12311
|
function resolveDaemonPaths(homeDir) {
|
|
12763
12312
|
const rootDir = join19(homeDir, ".evodev", "STATE", "daemon");
|
|
12764
12313
|
return { rootDir, lockPath: join19(rootDir, "lock.json"), tokenPath: join19(rootDir, "token") };
|
|
@@ -12801,14 +12350,14 @@ async function writeDaemonState(input) {
|
|
|
12801
12350
|
tokenPath: plan.paths.tokenPath,
|
|
12802
12351
|
versionText: input.versionText ?? "evodev 0.0.1-alpha"
|
|
12803
12352
|
};
|
|
12804
|
-
await
|
|
12805
|
-
await
|
|
12353
|
+
await mkdir12(dirname13(plan.paths.lockPath), { recursive: true });
|
|
12354
|
+
await writeFile11(plan.paths.tokenPath, `${token}
|
|
12806
12355
|
`, {
|
|
12807
12356
|
encoding: "utf8",
|
|
12808
12357
|
flag: "wx",
|
|
12809
12358
|
mode: 384
|
|
12810
12359
|
});
|
|
12811
|
-
await
|
|
12360
|
+
await writeFile11(plan.paths.lockPath, `${JSON.stringify(lock, null, 2)}
|
|
12812
12361
|
`, {
|
|
12813
12362
|
encoding: "utf8",
|
|
12814
12363
|
flag: "wx",
|
|
@@ -12818,7 +12367,7 @@ async function writeDaemonState(input) {
|
|
|
12818
12367
|
}
|
|
12819
12368
|
async function readDaemonLock(homeDir) {
|
|
12820
12369
|
const paths2 = resolveDaemonPaths(homeDir);
|
|
12821
|
-
if (!await
|
|
12370
|
+
if (!await pathExists6(paths2.lockPath))
|
|
12822
12371
|
return null;
|
|
12823
12372
|
const lock = JSON.parse(await readFile16(paths2.lockPath, "utf8"));
|
|
12824
12373
|
if (lock.version !== 1 || lock.component !== "evodev-daemon")
|
|
@@ -12827,7 +12376,7 @@ async function readDaemonLock(homeDir) {
|
|
|
12827
12376
|
}
|
|
12828
12377
|
async function readDaemonToken(homeDir) {
|
|
12829
12378
|
const paths2 = resolveDaemonPaths(homeDir);
|
|
12830
|
-
if (!await
|
|
12379
|
+
if (!await pathExists6(paths2.tokenPath))
|
|
12831
12380
|
return null;
|
|
12832
12381
|
return (await readFile16(paths2.tokenPath, "utf8")).trim();
|
|
12833
12382
|
}
|
|
@@ -12891,8 +12440,6 @@ async function handleDaemonRequest(input) {
|
|
|
12891
12440
|
}
|
|
12892
12441
|
if (input.method !== "GET")
|
|
12893
12442
|
return notFound(warnings);
|
|
12894
|
-
if (input.path === "/tasks")
|
|
12895
|
-
return ok(await collectTaskSummaries(input.homeDir, warnings), warnings);
|
|
12896
12443
|
if (input.path === "/observability/events")
|
|
12897
12444
|
return ok(await collectObservabilitySummaries(input.homeDir, warnings), warnings);
|
|
12898
12445
|
if (input.path === "/memory/candidates")
|
|
@@ -12942,14 +12489,14 @@ async function runDaemonForeground(input) {
|
|
|
12942
12489
|
}));
|
|
12943
12490
|
}
|
|
12944
12491
|
});
|
|
12945
|
-
await new Promise((
|
|
12492
|
+
await new Promise((resolve4, reject) => {
|
|
12946
12493
|
const onError = (error) => {
|
|
12947
12494
|
server.off("listening", onListening);
|
|
12948
12495
|
reject(error);
|
|
12949
12496
|
};
|
|
12950
12497
|
const onListening = () => {
|
|
12951
12498
|
server.off("error", onError);
|
|
12952
|
-
|
|
12499
|
+
resolve4();
|
|
12953
12500
|
};
|
|
12954
12501
|
server.once("error", onError);
|
|
12955
12502
|
server.once("listening", onListening);
|
|
@@ -12966,11 +12513,11 @@ async function runDaemonForeground(input) {
|
|
|
12966
12513
|
processEvolutionTriggers({ homeDir: input.homeDir, limit: 20 }).then(() => clearDaemonEvolutionProcessError(input.homeDir)).catch((error) => recordDaemonEvolutionProcessError(input.homeDir, error));
|
|
12967
12514
|
}, 5000);
|
|
12968
12515
|
evolutionInterval.unref();
|
|
12969
|
-
await new Promise((
|
|
12516
|
+
await new Promise((resolve4, reject) => {
|
|
12970
12517
|
server.once("close", () => {
|
|
12971
12518
|
clearInterval(reconcileInterval);
|
|
12972
12519
|
clearInterval(evolutionInterval);
|
|
12973
|
-
|
|
12520
|
+
resolve4();
|
|
12974
12521
|
});
|
|
12975
12522
|
server.once("error", reject);
|
|
12976
12523
|
});
|
|
@@ -13017,30 +12564,6 @@ function isAllowedLocalOrigin(origin) {
|
|
|
13017
12564
|
return false;
|
|
13018
12565
|
}
|
|
13019
12566
|
}
|
|
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
12567
|
async function collectObservabilitySummaries(homeDir, warnings) {
|
|
13045
12568
|
try {
|
|
13046
12569
|
return (await listObservabilityEvents(homeDir)).map((event) => sanitizeMetadata({
|
|
@@ -13057,7 +12580,7 @@ async function collectObservabilitySummaries(homeDir, warnings) {
|
|
|
13057
12580
|
}
|
|
13058
12581
|
async function collectLearningCandidateSummaries(homeDir, warnings) {
|
|
13059
12582
|
const path = join19(homeDir, ".evodev", "STATE", "learning", "candidates.jsonl");
|
|
13060
|
-
if (!await
|
|
12583
|
+
if (!await pathExists6(path)) {
|
|
13061
12584
|
warnings.push("Learning candidate store not found; returning empty candidates.");
|
|
13062
12585
|
return [];
|
|
13063
12586
|
}
|
|
@@ -13289,16 +12812,16 @@ async function resumeDashboardTeamRun(homeDir, body, runtimeAdapter) {
|
|
|
13289
12812
|
};
|
|
13290
12813
|
}
|
|
13291
12814
|
async function collectDirectorySummaries(root, warnings) {
|
|
13292
|
-
if (!await
|
|
12815
|
+
if (!await pathExists6(root)) {
|
|
13293
12816
|
warnings.push(`Store not found: ${root}`);
|
|
13294
12817
|
return [];
|
|
13295
12818
|
}
|
|
13296
|
-
const entries = await
|
|
12819
|
+
const entries = await readdir12(root, { withFileTypes: true });
|
|
13297
12820
|
return entries.filter((entry) => entry.isDirectory()).map((entry) => ({ id: entry.name, metadataOnly: true }));
|
|
13298
12821
|
}
|
|
13299
12822
|
function sanitizeMetadata(value) {
|
|
13300
12823
|
if (typeof value === "string") {
|
|
13301
|
-
if (
|
|
12824
|
+
if (SENSITIVE_TEXT_PATTERN5.test(value))
|
|
13302
12825
|
return "[redacted]";
|
|
13303
12826
|
return value.slice(0, 500);
|
|
13304
12827
|
}
|
|
@@ -13349,8 +12872,8 @@ function resolveDaemonEvolutionProcessErrorPath(homeDir) {
|
|
|
13349
12872
|
async function recordDaemonEvolutionProcessError(homeDir, error) {
|
|
13350
12873
|
try {
|
|
13351
12874
|
const path = resolveDaemonEvolutionProcessErrorPath(homeDir);
|
|
13352
|
-
await
|
|
13353
|
-
await
|
|
12875
|
+
await mkdir12(dirname13(path), { recursive: true });
|
|
12876
|
+
await writeFile11(path, `${JSON.stringify({
|
|
13354
12877
|
schemaVersion: 1,
|
|
13355
12878
|
kind: "daemon-evolution-process-error",
|
|
13356
12879
|
updatedAt: new Date().toISOString(),
|
|
@@ -13366,7 +12889,7 @@ async function clearDaemonEvolutionProcessError(homeDir) {
|
|
|
13366
12889
|
}
|
|
13367
12890
|
async function readDaemonEvolutionProcessError(homeDir) {
|
|
13368
12891
|
const path = resolveDaemonEvolutionProcessErrorPath(homeDir);
|
|
13369
|
-
if (!await
|
|
12892
|
+
if (!await pathExists6(path))
|
|
13370
12893
|
return null;
|
|
13371
12894
|
const value = JSON.parse(await readFile16(path, "utf8"));
|
|
13372
12895
|
return typeof value.summary === "string" && value.summary.length > 0 ? value.summary : null;
|
|
@@ -13374,25 +12897,13 @@ async function readDaemonEvolutionProcessError(homeDir) {
|
|
|
13374
12897
|
function describeError3(error) {
|
|
13375
12898
|
return error instanceof Error ? error.message : String(error);
|
|
13376
12899
|
}
|
|
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
12900
|
function ok(data, warnings) {
|
|
13390
12901
|
return { status: 200, body: { ok: true, data: sanitizeMetadata(data), warnings } };
|
|
13391
12902
|
}
|
|
13392
12903
|
function notFound(warnings) {
|
|
13393
12904
|
return { status: 404, body: { ok: false, data: { error: "not found" }, warnings } };
|
|
13394
12905
|
}
|
|
13395
|
-
async function
|
|
12906
|
+
async function pathExists6(path) {
|
|
13396
12907
|
try {
|
|
13397
12908
|
await stat9(path);
|
|
13398
12909
|
return true;
|
|
@@ -13418,7 +12929,7 @@ __export(exports_review, {
|
|
|
13418
12929
|
resolveLearningCandidateQueuePath: () => resolveLearningCandidateQueuePath,
|
|
13419
12930
|
readLearningReviewDecisions: () => readLearningReviewDecisions,
|
|
13420
12931
|
readLearningCandidates: () => readLearningCandidates,
|
|
13421
|
-
pathExists: () =>
|
|
12932
|
+
pathExists: () => pathExists7,
|
|
13422
12933
|
parseLearningReviewDecisionRecord: () => parseLearningReviewDecisionRecord,
|
|
13423
12934
|
parseLearningCandidate: () => parseLearningCandidate,
|
|
13424
12935
|
listLearningReviewDecisions: () => listLearningReviewDecisions,
|
|
@@ -13432,7 +12943,7 @@ __export(exports_review, {
|
|
|
13432
12943
|
appendLearningReviewDecision: () => appendLearningReviewDecision,
|
|
13433
12944
|
appendLearningCandidate: () => appendLearningCandidate
|
|
13434
12945
|
});
|
|
13435
|
-
import { mkdir as
|
|
12946
|
+
import { mkdir as mkdir13, readFile as readFile17, stat as stat10, writeFile as writeFile12 } from "node:fs/promises";
|
|
13436
12947
|
import { dirname as dirname14, join as join20 } from "node:path";
|
|
13437
12948
|
var LEARNING_CANDIDATE_KINDS = [
|
|
13438
12949
|
"lesson",
|
|
@@ -13455,7 +12966,7 @@ var LEARNING_CONFIDENCE_VALUES = ["low", "medium", "high"];
|
|
|
13455
12966
|
var LEARNING_DECISION_VALUES = ["rejected", "deferred"];
|
|
13456
12967
|
var LEARNING_DECIDED_BY_VALUES = ["user"];
|
|
13457
12968
|
var LEARNING_CANDIDATE_DECISION_STATUSES = ["rejected", "deferred"];
|
|
13458
|
-
var
|
|
12969
|
+
var FORBIDDEN_RAW_KEYS3 = new Set([
|
|
13459
12970
|
"commandhistory",
|
|
13460
12971
|
"commandoutput",
|
|
13461
12972
|
"credential",
|
|
@@ -13490,28 +13001,27 @@ var FORBIDDEN_RAW_KEYS4 = new Set([
|
|
|
13490
13001
|
"transcriptbody",
|
|
13491
13002
|
"transcripttext"
|
|
13492
13003
|
]);
|
|
13493
|
-
var
|
|
13004
|
+
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
13005
|
var PROTECTED_PATH_PATTERN = /(^|[~/\\])(?:USER|KNOWLEDGE|LEARNING|OBSERVABILITY|PACKS|RELEASES|logs?|memory|\.env[^/\\]*)(?:$|[/\\])|PROJECTS[/\\][^/\\]+[/\\]LEARNING(?:$|[/\\])|\.evodev[/\\](?:USER|KNOWLEDGE|LEARNING|OBSERVABILITY|PACKS|RELEASES)(?:$|[/\\])/i;
|
|
13495
13006
|
function createLearningCandidate(input) {
|
|
13496
13007
|
const candidate = {
|
|
13497
13008
|
version: 1,
|
|
13498
|
-
id:
|
|
13009
|
+
id: sanitizeId3(input.id),
|
|
13499
13010
|
kind: input.kind,
|
|
13500
13011
|
status: "candidate",
|
|
13501
13012
|
routingInfluence: false,
|
|
13502
13013
|
scope: sanitizeScope(input.scope),
|
|
13503
13014
|
content: {
|
|
13504
|
-
summary:
|
|
13505
|
-
howToApply:
|
|
13506
|
-
antiCriteriaImpact: input.content.antiCriteriaImpact.map(
|
|
13015
|
+
summary: sanitizeText3(input.content.summary),
|
|
13016
|
+
howToApply: sanitizeText3(input.content.howToApply),
|
|
13017
|
+
antiCriteriaImpact: input.content.antiCriteriaImpact.map(sanitizeText3)
|
|
13507
13018
|
},
|
|
13508
13019
|
provenance: {
|
|
13509
13020
|
taskId: sanitizeNullableId(input.provenance.taskId),
|
|
13510
|
-
taskContractRef: sanitizeNullableText(input.provenance.taskContractRef),
|
|
13511
13021
|
workflowRunId: sanitizeNullableId(input.provenance.workflowRunId),
|
|
13512
|
-
evidenceRefs: input.provenance.evidenceRefs.map(
|
|
13022
|
+
evidenceRefs: input.provenance.evidenceRefs.map(sanitizeText3),
|
|
13513
13023
|
sourceType: input.provenance.sourceType,
|
|
13514
|
-
createdAt:
|
|
13024
|
+
createdAt: sanitizeText3(input.provenance.createdAt),
|
|
13515
13025
|
createdBy: input.provenance.createdBy,
|
|
13516
13026
|
rawPromptStored: false,
|
|
13517
13027
|
sourceContentStored: false,
|
|
@@ -13530,7 +13040,7 @@ function createLearningCandidate(input) {
|
|
|
13530
13040
|
return candidate;
|
|
13531
13041
|
}
|
|
13532
13042
|
function validateLearningCandidate(candidate) {
|
|
13533
|
-
if (!
|
|
13043
|
+
if (!isRecord10(candidate))
|
|
13534
13044
|
throw new Error("Learning candidate must be an object.");
|
|
13535
13045
|
if (candidate.version !== 1)
|
|
13536
13046
|
throw new Error("Learning candidate version must be 1.");
|
|
@@ -13542,33 +13052,33 @@ function validateLearningCandidate(candidate) {
|
|
|
13542
13052
|
if (candidate.routingInfluence !== false) {
|
|
13543
13053
|
throw new Error("Learning candidate routingInfluence must be false in I5.");
|
|
13544
13054
|
}
|
|
13545
|
-
if (!
|
|
13055
|
+
if (!isRecord10(candidate.scope))
|
|
13546
13056
|
throw new Error("Learning candidate scope must be an object.");
|
|
13547
13057
|
assertEnumValue("scope.level", candidate.scope.level, LEARNING_SCOPE_LEVELS);
|
|
13548
|
-
if (!
|
|
13058
|
+
if (!isRecord10(candidate.content)) {
|
|
13549
13059
|
throw new Error("Learning candidate content must be an object.");
|
|
13550
13060
|
}
|
|
13551
13061
|
assertStringField("content.summary", candidate.content.summary);
|
|
13552
13062
|
assertStringField("content.howToApply", candidate.content.howToApply);
|
|
13553
13063
|
assertStringArrayField("content.antiCriteriaImpact", candidate.content.antiCriteriaImpact);
|
|
13554
|
-
if (!
|
|
13064
|
+
if (!isRecord10(candidate.provenance)) {
|
|
13555
13065
|
throw new Error("Learning candidate provenance must be an object.");
|
|
13556
13066
|
}
|
|
13557
13067
|
assertEnumValue("provenance.sourceType", candidate.provenance.sourceType, LEARNING_SOURCE_TYPES);
|
|
13558
13068
|
assertEnumValue("provenance.createdBy", candidate.provenance.createdBy, LEARNING_CREATED_BY_VALUES);
|
|
13559
13069
|
assertStringField("provenance.createdAt", candidate.provenance.createdAt);
|
|
13560
13070
|
assertStringArrayField("provenance.evidenceRefs", candidate.provenance.evidenceRefs);
|
|
13561
|
-
if (!
|
|
13071
|
+
if (!isRecord10(candidate.privacy)) {
|
|
13562
13072
|
throw new Error("Learning candidate privacy must be an object.");
|
|
13563
13073
|
}
|
|
13564
13074
|
assertEnumValue("privacy.classification", candidate.privacy.classification, ["local-private"]);
|
|
13565
|
-
if (!
|
|
13075
|
+
if (!isRecord10(candidate.review))
|
|
13566
13076
|
throw new Error("Learning candidate review must be an object.");
|
|
13567
13077
|
assertEnumValue("review.decision", candidate.review.decision, LEARNING_REVIEW_DECISIONS);
|
|
13568
13078
|
if (candidate.review.decision !== "pending") {
|
|
13569
13079
|
throw new Error("Learning candidate review decision must be pending.");
|
|
13570
13080
|
}
|
|
13571
|
-
if (!
|
|
13081
|
+
if (!isRecord10(candidate.retention)) {
|
|
13572
13082
|
throw new Error("Learning candidate retention must be an object.");
|
|
13573
13083
|
}
|
|
13574
13084
|
assertEnumValue("confidence", candidate.confidence, LEARNING_CONFIDENCE_VALUES);
|
|
@@ -13584,7 +13094,7 @@ function validateLearningCandidate(candidate) {
|
|
|
13584
13094
|
assertNoForbiddenContent2(candidate);
|
|
13585
13095
|
}
|
|
13586
13096
|
function validateLearningReviewDecisionRecord(record) {
|
|
13587
|
-
if (!
|
|
13097
|
+
if (!isRecord10(record))
|
|
13588
13098
|
throw new Error("Learning review decision must be an object.");
|
|
13589
13099
|
if (record.version !== 1)
|
|
13590
13100
|
throw new Error("Learning review decision version must be 1.");
|
|
@@ -13607,13 +13117,13 @@ function validateLearningReviewDecisionRecord(record) {
|
|
|
13607
13117
|
assertNoForbiddenContent2(record);
|
|
13608
13118
|
}
|
|
13609
13119
|
function parseLearningCandidate(value) {
|
|
13610
|
-
if (!
|
|
13120
|
+
if (!isRecord10(value))
|
|
13611
13121
|
throw new Error("Invalid learning candidate JSON.");
|
|
13612
13122
|
validateLearningCandidate(value);
|
|
13613
13123
|
return value;
|
|
13614
13124
|
}
|
|
13615
13125
|
function parseLearningReviewDecisionRecord(value) {
|
|
13616
|
-
if (!
|
|
13126
|
+
if (!isRecord10(value))
|
|
13617
13127
|
throw new Error("Invalid learning review decision JSON.");
|
|
13618
13128
|
validateLearningReviewDecisionRecord(value);
|
|
13619
13129
|
return value;
|
|
@@ -13633,20 +13143,20 @@ function resolveLearningDecisionPath(homeDir) {
|
|
|
13633
13143
|
async function appendLearningCandidate(homeDir, candidate) {
|
|
13634
13144
|
validateLearningCandidate(candidate);
|
|
13635
13145
|
const path = resolveLearningCandidateQueuePath(homeDir);
|
|
13636
|
-
await
|
|
13637
|
-
await
|
|
13146
|
+
await mkdir13(dirname14(path), { recursive: true });
|
|
13147
|
+
await writeFile12(path, `${JSON.stringify(candidate)}
|
|
13638
13148
|
`, { encoding: "utf8", flag: "a" });
|
|
13639
13149
|
return path;
|
|
13640
13150
|
}
|
|
13641
13151
|
async function listLearningCandidates(homeDir) {
|
|
13642
13152
|
const path = resolveLearningCandidateQueuePath(homeDir);
|
|
13643
|
-
if (!await
|
|
13153
|
+
if (!await pathExists7(path))
|
|
13644
13154
|
return [];
|
|
13645
13155
|
return readLearningCandidates(path);
|
|
13646
13156
|
}
|
|
13647
13157
|
async function listLearningReviewDecisions(homeDir) {
|
|
13648
13158
|
const path = resolveLearningDecisionPath(homeDir);
|
|
13649
|
-
if (!await
|
|
13159
|
+
if (!await pathExists7(path))
|
|
13650
13160
|
return [];
|
|
13651
13161
|
return readLearningReviewDecisions(path);
|
|
13652
13162
|
}
|
|
@@ -13706,7 +13216,7 @@ function lintLearningCandidates(candidates2, options = {}) {
|
|
|
13706
13216
|
validateLearningReviewDecisionRecord(decision);
|
|
13707
13217
|
} catch (error) {
|
|
13708
13218
|
findings.push({
|
|
13709
|
-
candidateId:
|
|
13219
|
+
candidateId: isRecord10(decision) && typeof decision.candidateId === "string" ? decision.candidateId : "unknown",
|
|
13710
13220
|
severity: "error",
|
|
13711
13221
|
field: "decision",
|
|
13712
13222
|
message: error instanceof Error ? error.message : String(error)
|
|
@@ -13778,9 +13288,9 @@ function createLearningReviewDecisionRecord(input) {
|
|
|
13778
13288
|
const candidateStatus = input.decision === "rejected" ? "rejected" : "deferred";
|
|
13779
13289
|
const record = {
|
|
13780
13290
|
version: 1,
|
|
13781
|
-
candidateId:
|
|
13291
|
+
candidateId: sanitizeId3(input.candidateId),
|
|
13782
13292
|
decision: input.decision,
|
|
13783
|
-
decidedAt:
|
|
13293
|
+
decidedAt: sanitizeText3(input.decidedAt ?? new Date().toISOString()),
|
|
13784
13294
|
decidedBy: "user",
|
|
13785
13295
|
reason: input.reason === undefined ? null : sanitizeNullableText(input.reason),
|
|
13786
13296
|
writesAcceptedMemory: false,
|
|
@@ -13793,12 +13303,12 @@ function createLearningReviewDecisionRecord(input) {
|
|
|
13793
13303
|
async function appendLearningReviewDecision(homeDir, record) {
|
|
13794
13304
|
validateLearningReviewDecisionRecord(record);
|
|
13795
13305
|
const path = resolveLearningDecisionPath(homeDir);
|
|
13796
|
-
await
|
|
13797
|
-
await
|
|
13306
|
+
await mkdir13(dirname14(path), { recursive: true });
|
|
13307
|
+
await writeFile12(path, `${JSON.stringify(record)}
|
|
13798
13308
|
`, { encoding: "utf8", flag: "a" });
|
|
13799
13309
|
return path;
|
|
13800
13310
|
}
|
|
13801
|
-
async function
|
|
13311
|
+
async function pathExists7(path) {
|
|
13802
13312
|
try {
|
|
13803
13313
|
await stat10(path);
|
|
13804
13314
|
return true;
|
|
@@ -13860,13 +13370,7 @@ async function parseJsonOrJsonlFile(path, parseItem) {
|
|
|
13860
13370
|
return values.map(parseItem);
|
|
13861
13371
|
}
|
|
13862
13372
|
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");
|
|
13373
|
+
return candidate.provenance.evidenceRefs.map((ref, index) => [`provenance.evidenceRefs[${index}]`, ref]).filter((entry) => typeof entry[1] === "string");
|
|
13870
13374
|
}
|
|
13871
13375
|
function isCandidateStale(candidate, now) {
|
|
13872
13376
|
if (candidate.retention.staleAfter === null)
|
|
@@ -13894,7 +13398,7 @@ function assertNoForbiddenContent2(value) {
|
|
|
13894
13398
|
if (typeof value === "string") {
|
|
13895
13399
|
if (value === "local-private")
|
|
13896
13400
|
return;
|
|
13897
|
-
if (
|
|
13401
|
+
if (SENSITIVE_TEXT_PATTERN6.test(value)) {
|
|
13898
13402
|
throw new Error("Learning candidate contains sensitive content.");
|
|
13899
13403
|
}
|
|
13900
13404
|
return;
|
|
@@ -13904,38 +13408,38 @@ function assertNoForbiddenContent2(value) {
|
|
|
13904
13408
|
assertNoForbiddenContent2(item);
|
|
13905
13409
|
return;
|
|
13906
13410
|
}
|
|
13907
|
-
if (!
|
|
13411
|
+
if (!isRecord10(value))
|
|
13908
13412
|
return;
|
|
13909
13413
|
for (const [key, child] of Object.entries(value)) {
|
|
13910
13414
|
const normalizedKey = normalizeKey2(key);
|
|
13911
|
-
if (
|
|
13415
|
+
if (FORBIDDEN_RAW_KEYS3.has(normalizedKey)) {
|
|
13912
13416
|
throw new Error(`Learning candidate contains forbidden raw field: ${key}`);
|
|
13913
13417
|
}
|
|
13914
13418
|
assertNoForbiddenContent2(child);
|
|
13915
13419
|
}
|
|
13916
13420
|
}
|
|
13917
|
-
function
|
|
13918
|
-
return value.replace(
|
|
13421
|
+
function sanitizeText3(value) {
|
|
13422
|
+
return value.replace(SENSITIVE_TEXT_PATTERN6, "[redacted]").slice(0, 500);
|
|
13919
13423
|
}
|
|
13920
13424
|
function sanitizeNullableText(value) {
|
|
13921
|
-
return value === null ? null :
|
|
13425
|
+
return value === null ? null : sanitizeText3(value);
|
|
13922
13426
|
}
|
|
13923
|
-
function
|
|
13924
|
-
const sanitized =
|
|
13427
|
+
function sanitizeId3(value) {
|
|
13428
|
+
const sanitized = sanitizeText3(value).replace(/\[redacted\]/gi, "redacted").replace(/[^a-zA-Z0-9._-]/g, "-").replace(/-+/g, "-").replace(/^-+|-+$/g, "").slice(0, 100);
|
|
13925
13429
|
return sanitized || "learning-candidate";
|
|
13926
13430
|
}
|
|
13927
13431
|
function sanitizeNullableId(value) {
|
|
13928
|
-
return value === null ? null :
|
|
13432
|
+
return value === null ? null : sanitizeId3(value);
|
|
13929
13433
|
}
|
|
13930
13434
|
function normalizeKey2(key) {
|
|
13931
13435
|
return key.toLowerCase().replace(/[^a-z0-9]/g, "");
|
|
13932
13436
|
}
|
|
13933
|
-
function
|
|
13437
|
+
function isRecord10(value) {
|
|
13934
13438
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
13935
13439
|
}
|
|
13936
13440
|
// packages/core/src/pack/index.ts
|
|
13937
|
-
import { readFile as readFile18, readdir as
|
|
13938
|
-
import { isAbsolute as
|
|
13441
|
+
import { readFile as readFile18, readdir as readdir13, stat as stat11 } from "node:fs/promises";
|
|
13442
|
+
import { isAbsolute as isAbsolute7, join as join21, relative as relative7, sep } from "node:path";
|
|
13939
13443
|
|
|
13940
13444
|
// packages/core/src/protected-zones/index.ts
|
|
13941
13445
|
var SENSITIVE_DIRECTORY_SEGMENTS = new Set([
|
|
@@ -14170,7 +13674,7 @@ async function planPackInstallDryRun(input) {
|
|
|
14170
13674
|
};
|
|
14171
13675
|
}
|
|
14172
13676
|
function parsePackManifest(value) {
|
|
14173
|
-
if (!
|
|
13677
|
+
if (!isRecord11(value))
|
|
14174
13678
|
throw new Error("Pack manifest must be an object.");
|
|
14175
13679
|
const manifest = value;
|
|
14176
13680
|
validateManifestTopLevelFields(manifest);
|
|
@@ -14180,14 +13684,14 @@ function parsePackManifest(value) {
|
|
|
14180
13684
|
const description = requireString(manifest, "description");
|
|
14181
13685
|
const publisher = optionalString6(manifest, "publisher");
|
|
14182
13686
|
const license = optionalString6(manifest, "license");
|
|
14183
|
-
const compatibility =
|
|
14184
|
-
const assets = parseAssets(
|
|
13687
|
+
const compatibility = requireRecord2(manifest, "compatibility");
|
|
13688
|
+
const assets = parseAssets(requireRecord2(manifest, "assets"));
|
|
14185
13689
|
const permissions = parsePermissions(manifest.permissions);
|
|
14186
|
-
const install =
|
|
14187
|
-
const verify =
|
|
14188
|
-
const customizations =
|
|
14189
|
-
const observability =
|
|
14190
|
-
const uninstall =
|
|
13690
|
+
const install = requireRecord2(manifest, "install");
|
|
13691
|
+
const verify = requireRecord2(manifest, "verify");
|
|
13692
|
+
const customizations = requireRecord2(manifest, "customizations");
|
|
13693
|
+
const observability = requireRecord2(manifest, "observability");
|
|
13694
|
+
const uninstall = requireRecord2(manifest, "uninstall");
|
|
14191
13695
|
validateCompatibilityTargets(id, requireStringArray(compatibility, "targets"));
|
|
14192
13696
|
validateProtectedZoneDeclarations(manifest.protectedZones);
|
|
14193
13697
|
if (observability.metadataOnly !== true) {
|
|
@@ -14225,7 +13729,7 @@ function parsePackManifest(value) {
|
|
|
14225
13729
|
userPath: requireString(customizations, "userPath"),
|
|
14226
13730
|
projectPath: requireString(customizations, "projectPath")
|
|
14227
13731
|
},
|
|
14228
|
-
protectedZones:
|
|
13732
|
+
protectedZones: isRecord11(manifest.protectedZones) ? {
|
|
14229
13733
|
neverInclude: optionalStringArray(manifest.protectedZones, "neverInclude"),
|
|
14230
13734
|
neverWrite: optionalStringArray(manifest.protectedZones, "neverWrite")
|
|
14231
13735
|
} : undefined,
|
|
@@ -14415,7 +13919,7 @@ function validateGuideReferences(manifest, assets, findings) {
|
|
|
14415
13919
|
}
|
|
14416
13920
|
}
|
|
14417
13921
|
async function collectPackRelativePaths(packRoot, dir = packRoot) {
|
|
14418
|
-
const entries = await
|
|
13922
|
+
const entries = await readdir13(dir, { withFileTypes: true });
|
|
14419
13923
|
const paths3 = [];
|
|
14420
13924
|
for (const entry of entries) {
|
|
14421
13925
|
const absolutePath = join21(dir, entry.name);
|
|
@@ -14434,7 +13938,7 @@ function validatePackRelativePath(path, packRoot) {
|
|
|
14434
13938
|
if (path.includes("\x00")) {
|
|
14435
13939
|
return { severity: "error", code: "path-invalid", message: "Path must not contain NUL bytes." };
|
|
14436
13940
|
}
|
|
14437
|
-
if (
|
|
13941
|
+
if (isAbsolute7(path) || path.startsWith("~")) {
|
|
14438
13942
|
return {
|
|
14439
13943
|
severity: "error",
|
|
14440
13944
|
code: "path-absolute",
|
|
@@ -14451,7 +13955,7 @@ function validatePackRelativePath(path, packRoot) {
|
|
|
14451
13955
|
}
|
|
14452
13956
|
const absolute = join21(packRoot, normalized);
|
|
14453
13957
|
const rel = relative7(packRoot, absolute);
|
|
14454
|
-
if (rel === "" || rel.startsWith("..") ||
|
|
13958
|
+
if (rel === "" || rel.startsWith("..") || isAbsolute7(rel)) {
|
|
14455
13959
|
return {
|
|
14456
13960
|
severity: "error",
|
|
14457
13961
|
code: "path-traversal",
|
|
@@ -14496,7 +14000,7 @@ function parseAssets(value) {
|
|
|
14496
14000
|
function parsePermissions(value) {
|
|
14497
14001
|
if (value === undefined)
|
|
14498
14002
|
return {};
|
|
14499
|
-
if (!
|
|
14003
|
+
if (!isRecord11(value))
|
|
14500
14004
|
throw new Error("Pack manifest permissions must be an object.");
|
|
14501
14005
|
const permissions = {};
|
|
14502
14006
|
const knownKeys = new Set(Object.keys(RISKY_PERMISSION_LABELS));
|
|
@@ -14532,9 +14036,9 @@ function requireBoolean(record, key) {
|
|
|
14532
14036
|
throw new Error(`Pack manifest missing boolean field: ${key}`);
|
|
14533
14037
|
return value;
|
|
14534
14038
|
}
|
|
14535
|
-
function
|
|
14039
|
+
function requireRecord2(record, key) {
|
|
14536
14040
|
const value = record[key];
|
|
14537
|
-
if (!
|
|
14041
|
+
if (!isRecord11(value))
|
|
14538
14042
|
throw new Error(`Pack manifest missing object field: ${key}`);
|
|
14539
14043
|
return value;
|
|
14540
14044
|
}
|
|
@@ -14590,7 +14094,7 @@ function validateCompatibilityTargets(packId, targets) {
|
|
|
14590
14094
|
function validateProtectedZoneDeclarations(value) {
|
|
14591
14095
|
if (value === undefined)
|
|
14592
14096
|
return;
|
|
14593
|
-
if (!
|
|
14097
|
+
if (!isRecord11(value))
|
|
14594
14098
|
throw new Error("Pack manifest protectedZones must be an object.");
|
|
14595
14099
|
const knownFields = new Set(["neverInclude", "neverWrite"]);
|
|
14596
14100
|
for (const key of Object.keys(value)) {
|
|
@@ -14603,7 +14107,7 @@ function validateProtectedZoneDeclarations(value) {
|
|
|
14603
14107
|
function isRemotePackInput(packPath) {
|
|
14604
14108
|
return /^[a-z][a-z0-9+.-]*:\/\//i.test(packPath);
|
|
14605
14109
|
}
|
|
14606
|
-
function
|
|
14110
|
+
function isRecord11(value) {
|
|
14607
14111
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
14608
14112
|
}
|
|
14609
14113
|
function normalizeRelativePath(path) {
|
|
@@ -14616,7 +14120,7 @@ function formatError(error) {
|
|
|
14616
14120
|
return error instanceof Error ? error.message : String(error);
|
|
14617
14121
|
}
|
|
14618
14122
|
// packages/core/src/plugins/capabilities.ts
|
|
14619
|
-
import { mkdir as
|
|
14123
|
+
import { mkdir as mkdir14, readFile as readFile19, writeFile as writeFile13 } from "node:fs/promises";
|
|
14620
14124
|
import { dirname as dirname15, join as join22 } from "node:path";
|
|
14621
14125
|
function createUnknownNegotiatedCapabilities(pluginId) {
|
|
14622
14126
|
return {
|
|
@@ -14669,7 +14173,7 @@ function resolveNonCodexCapabilityState(supported, enabled) {
|
|
|
14669
14173
|
}
|
|
14670
14174
|
async function createCodexCapabilityVerificationArtifact(input) {
|
|
14671
14175
|
const timestamp = input.createdAt ?? new Date().toISOString();
|
|
14672
|
-
const detectionMessage =
|
|
14176
|
+
const detectionMessage = sanitizeText4(input.detection.message);
|
|
14673
14177
|
const diagnostics = [
|
|
14674
14178
|
"Codex skills are packaged in the EvoDev Codex plugin payload.",
|
|
14675
14179
|
"Codex agents sync to user-level TOML files under ~/.codex/agents.",
|
|
@@ -14732,8 +14236,8 @@ function resolveCodexCapabilityArtifactPath(homeDir) {
|
|
|
14732
14236
|
async function writeCodexCapabilityVerificationArtifact(homeDir, artifact) {
|
|
14733
14237
|
validateCodexCapabilityVerificationArtifact(artifact);
|
|
14734
14238
|
const path = resolveCodexCapabilityArtifactPath(homeDir);
|
|
14735
|
-
await
|
|
14736
|
-
await
|
|
14239
|
+
await mkdir14(dirname15(path), { recursive: true });
|
|
14240
|
+
await writeFile13(path, `${JSON.stringify(artifact, null, 2)}
|
|
14737
14241
|
`, { encoding: "utf8", flag: "wx" });
|
|
14738
14242
|
return path;
|
|
14739
14243
|
}
|
|
@@ -14795,7 +14299,7 @@ async function runPluginConformance(plugin) {
|
|
|
14795
14299
|
}
|
|
14796
14300
|
return { ok: findings.length === 0, findings };
|
|
14797
14301
|
}
|
|
14798
|
-
function
|
|
14302
|
+
function sanitizeText4(value) {
|
|
14799
14303
|
return value.replace(/https?:\/\/\S+|\b(secret|token|password|private|internal|api[_-]?key)\b/gi, "[redacted]").slice(0, 300);
|
|
14800
14304
|
}
|
|
14801
14305
|
function assertNoSensitiveContent(value) {
|
|
@@ -14864,364 +14368,8 @@ function createPluginRegistry(plugins = []) {
|
|
|
14864
14368
|
function getEnabledPluginIds(settings) {
|
|
14865
14369
|
return Object.entries(settings.plugins).filter(([, pluginSettings]) => pluginSettings.enabled).map(([pluginId]) => pluginId).sort((left, right) => left.localeCompare(right));
|
|
14866
14370
|
}
|
|
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
14371
|
// packages/core/src/sync/orchestrator.ts
|
|
15224
|
-
import { join as
|
|
14372
|
+
import { join as join23 } from "node:path";
|
|
15225
14373
|
async function runSync(options) {
|
|
15226
14374
|
const store = createCoreConfigStore(options.homeDir);
|
|
15227
14375
|
const settings = await store.readSettings();
|
|
@@ -15364,8 +14512,8 @@ function filterAssetsForTarget(assets, targetPlugin) {
|
|
|
15364
14512
|
}
|
|
15365
14513
|
function resolveAssetScannerPaths(assetsRootDir) {
|
|
15366
14514
|
return {
|
|
15367
|
-
skillsDir:
|
|
15368
|
-
agentsDir:
|
|
14515
|
+
skillsDir: join23(assetsRootDir, "skills"),
|
|
14516
|
+
agentsDir: join23(assetsRootDir, "agents")
|
|
15369
14517
|
};
|
|
15370
14518
|
}
|
|
15371
14519
|
async function readRegistryOrDefault(store) {
|
|
@@ -15555,7 +14703,7 @@ async function handleTeamsMcpLine(line, options = {}) {
|
|
|
15555
14703
|
try {
|
|
15556
14704
|
return await handleTeamsMcpMessage(JSON.parse(line), options);
|
|
15557
14705
|
} catch (error) {
|
|
15558
|
-
return jsonRpcError(null, -32700, "Parse error",
|
|
14706
|
+
return jsonRpcError(null, -32700, "Parse error", describeError4(error));
|
|
15559
14707
|
}
|
|
15560
14708
|
}
|
|
15561
14709
|
async function handleTeamsMcpMessage(message, options = {}) {
|
|
@@ -15575,7 +14723,7 @@ async function handleTeamsMcpMessage(message, options = {}) {
|
|
|
15575
14723
|
}
|
|
15576
14724
|
return jsonRpcError(request.id, -32601, `Method not found: ${request.method}`);
|
|
15577
14725
|
} catch (error) {
|
|
15578
|
-
return jsonRpcError(request.id, -32603,
|
|
14726
|
+
return jsonRpcError(request.id, -32603, describeError4(error));
|
|
15579
14727
|
}
|
|
15580
14728
|
}
|
|
15581
14729
|
async function runTeamsMcpStdioServer(options = {}) {
|
|
@@ -15593,7 +14741,7 @@ async function runTeamsMcpStdioServer(options = {}) {
|
|
|
15593
14741
|
}
|
|
15594
14742
|
}
|
|
15595
14743
|
function initializeResult(request) {
|
|
15596
|
-
const params =
|
|
14744
|
+
const params = isRecord12(request.params) ? request.params : {};
|
|
15597
14745
|
const requestedVersion = typeof params.protocolVersion === "string" ? params.protocolVersion : PROTOCOL_VERSION;
|
|
15598
14746
|
const protocolVersion = SUPPORTED_PROTOCOL_VERSIONS.has(requestedVersion) ? requestedVersion : PROTOCOL_VERSION;
|
|
15599
14747
|
return {
|
|
@@ -15675,7 +14823,7 @@ function toolResult(structuredContent, isError = false) {
|
|
|
15675
14823
|
};
|
|
15676
14824
|
}
|
|
15677
14825
|
function parseRequest(message) {
|
|
15678
|
-
if (!
|
|
14826
|
+
if (!isRecord12(message))
|
|
15679
14827
|
throw new Error("Invalid JSON-RPC message; expected object.");
|
|
15680
14828
|
if (message.jsonrpc !== "2.0")
|
|
15681
14829
|
throw new Error("Invalid JSON-RPC version.");
|
|
@@ -15702,7 +14850,7 @@ function jsonRpcError(id, code, message, data) {
|
|
|
15702
14850
|
return { jsonrpc: "2.0", id, error: { code, message, data } };
|
|
15703
14851
|
}
|
|
15704
14852
|
function expectRecord5(value, path) {
|
|
15705
|
-
if (!
|
|
14853
|
+
if (!isRecord12(value))
|
|
15706
14854
|
throw new Error(`Invalid ${path}; expected object.`);
|
|
15707
14855
|
return value;
|
|
15708
14856
|
}
|
|
@@ -15715,28 +14863,28 @@ function expectString3(value, path) {
|
|
|
15715
14863
|
function optionalString7(value) {
|
|
15716
14864
|
return typeof value === "string" && value.length > 0 ? value : undefined;
|
|
15717
14865
|
}
|
|
15718
|
-
function
|
|
14866
|
+
function isRecord12(value) {
|
|
15719
14867
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
15720
14868
|
}
|
|
15721
|
-
function
|
|
14869
|
+
function describeError4(error) {
|
|
15722
14870
|
return error instanceof Error ? error.message : String(error);
|
|
15723
14871
|
}
|
|
15724
14872
|
// packages/core/src/workflow/index.ts
|
|
15725
|
-
import { readFile as
|
|
15726
|
-
import { join as
|
|
14873
|
+
import { readFile as readFile20, readdir as readdir14 } from "node:fs/promises";
|
|
14874
|
+
import { join as join24 } from "node:path";
|
|
15727
14875
|
async function scanWorkflowManifests(workflowsDir) {
|
|
15728
14876
|
const manifests = [];
|
|
15729
14877
|
const entries = await readdir14(workflowsDir, { withFileTypes: true });
|
|
15730
14878
|
for (const entry of entries) {
|
|
15731
14879
|
if (!entry.isDirectory())
|
|
15732
14880
|
continue;
|
|
15733
|
-
const manifestPath =
|
|
15734
|
-
manifests.push(parseWorkflowManifest(JSON.parse(await
|
|
14881
|
+
const manifestPath = join24(workflowsDir, entry.name, "WORKFLOW.json");
|
|
14882
|
+
manifests.push(parseWorkflowManifest(JSON.parse(await readFile20(manifestPath, "utf8"))));
|
|
15735
14883
|
}
|
|
15736
14884
|
return manifests.sort((left, right) => left.id.localeCompare(right.id));
|
|
15737
14885
|
}
|
|
15738
14886
|
function parseWorkflowManifest(value) {
|
|
15739
|
-
if (!
|
|
14887
|
+
if (!isRecord13(value))
|
|
15740
14888
|
throw new Error("Workflow manifest must be an object.");
|
|
15741
14889
|
const manifest = value;
|
|
15742
14890
|
if (typeof manifest.id !== "string" || typeof manifest.version !== "string") {
|
|
@@ -15751,20 +14899,12 @@ function parseWorkflowManifest(value) {
|
|
|
15751
14899
|
return manifest;
|
|
15752
14900
|
}
|
|
15753
14901
|
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
14902
|
return {
|
|
15761
14903
|
workflow: input.workflow,
|
|
15762
|
-
taskId: input.contract?.taskId,
|
|
15763
|
-
mode,
|
|
15764
14904
|
steps: input.workflow.steps.map((step) => ({ ...step, plannedOnly: true })),
|
|
15765
14905
|
requiredEvidence: input.workflow.requiredEvidence,
|
|
15766
|
-
warnings,
|
|
15767
|
-
advisories
|
|
14906
|
+
warnings: [],
|
|
14907
|
+
advisories: []
|
|
15768
14908
|
};
|
|
15769
14909
|
}
|
|
15770
14910
|
function formatWorkflowList(manifests) {
|
|
@@ -15780,8 +14920,6 @@ function formatWorkflowPlan(plan) {
|
|
|
15780
14920
|
"EvoDev workflow dry-run",
|
|
15781
14921
|
"",
|
|
15782
14922
|
`Workflow: ${plan.workflow.id}`,
|
|
15783
|
-
`Task: ${plan.taskId ?? "none"}`,
|
|
15784
|
-
`Mode: ${plan.mode ?? "not routed"}`,
|
|
15785
14923
|
"",
|
|
15786
14924
|
"Steps:",
|
|
15787
14925
|
...plan.steps.map((step) => ` - ${step.id}: ${step.name} [${step.actor}] evidence=${step.evidence.join(",") || "none"}`),
|
|
@@ -15793,13 +14931,11 @@ function formatWorkflowPlan(plan) {
|
|
|
15793
14931
|
].join(`
|
|
15794
14932
|
`);
|
|
15795
14933
|
}
|
|
15796
|
-
function
|
|
14934
|
+
function isRecord13(value) {
|
|
15797
14935
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
15798
14936
|
}
|
|
15799
14937
|
export {
|
|
15800
|
-
writeTaskContract,
|
|
15801
14938
|
writeSessionIndex,
|
|
15802
|
-
writeProjectContext,
|
|
15803
14939
|
writeJson2 as writeJson,
|
|
15804
14940
|
writeFailedOkfKnowledgePlanArtifact,
|
|
15805
14941
|
writeEvolutionRepoProposal,
|
|
@@ -15809,7 +14945,6 @@ export {
|
|
|
15809
14945
|
writeCodexCapabilityVerificationArtifact,
|
|
15810
14946
|
writeCodeAgentTraceRef,
|
|
15811
14947
|
withEvolutionReviewDecisionLock,
|
|
15812
|
-
verifyTaskContract,
|
|
15813
14948
|
validatePack,
|
|
15814
14949
|
validateOkfKnowledgePlanContract,
|
|
15815
14950
|
validateObservabilityEvent,
|
|
@@ -15848,7 +14983,6 @@ export {
|
|
|
15848
14983
|
runSync,
|
|
15849
14984
|
runPluginConformance,
|
|
15850
14985
|
runDaemonForeground,
|
|
15851
|
-
routeTaskContract,
|
|
15852
14986
|
revokeOkfKnowledgeConcept,
|
|
15853
14987
|
resumeTeamRun,
|
|
15854
14988
|
resumeFailedOkfKnowledgePlan,
|
|
@@ -15858,9 +14992,10 @@ export {
|
|
|
15858
14992
|
resolveTeamRole,
|
|
15859
14993
|
resolveTeamOverlay,
|
|
15860
14994
|
resolveTeamAgentReference,
|
|
15861
|
-
resolveTaskContractOutputPath,
|
|
15862
14995
|
resolveSessionMemoryPaths,
|
|
15863
14996
|
resolveSegmentEvolutionTriggerPath,
|
|
14997
|
+
resolveProjectWorkspaceFromCwd,
|
|
14998
|
+
resolveProjectRegistryPaths,
|
|
15864
14999
|
resolveProjectLogKey,
|
|
15865
15000
|
resolveOkfKnowledgePaths,
|
|
15866
15001
|
resolveObservabilityStorePaths,
|
|
@@ -15877,13 +15012,14 @@ export {
|
|
|
15877
15012
|
resolveCodeAgentTraceRefPaths,
|
|
15878
15013
|
renderRoleAgentDefinitionContext,
|
|
15879
15014
|
renderMainTeamOverlayContext,
|
|
15015
|
+
registerDiscoveredProject,
|
|
15880
15016
|
recordTeamAgentNativeSession,
|
|
15017
|
+
recordDiscoveredProject,
|
|
15881
15018
|
recordCodeAgentTraceRefFromHook,
|
|
15882
15019
|
reconcileTeamRun,
|
|
15883
15020
|
rebuildOkfKnowledgeIndexes,
|
|
15884
15021
|
readTeamAgentSummary,
|
|
15885
15022
|
readTeamAgentDefinition,
|
|
15886
|
-
readTaskContract,
|
|
15887
15023
|
readSessionState,
|
|
15888
15024
|
readSessionEvidenceSegment,
|
|
15889
15025
|
readSessionCursor,
|
|
@@ -15909,7 +15045,7 @@ export {
|
|
|
15909
15045
|
processEvolutionTriggers,
|
|
15910
15046
|
planWorkflow,
|
|
15911
15047
|
planPackInstallDryRun,
|
|
15912
|
-
|
|
15048
|
+
pathExists7 as pathExists,
|
|
15913
15049
|
parseWorkflowManifest,
|
|
15914
15050
|
parseTeamRoleDefinition,
|
|
15915
15051
|
parseTeamDefinitionMarkdown,
|
|
@@ -15936,14 +15072,17 @@ export {
|
|
|
15936
15072
|
mergeAcceptedEvosCasesIntoKnowledgeQuery,
|
|
15937
15073
|
markTeamMessagesDelivered,
|
|
15938
15074
|
markOkfKnowledgeConceptStale,
|
|
15075
|
+
markEvolutionRepoProposalApplied,
|
|
15939
15076
|
loadAgentContextDryRun,
|
|
15940
15077
|
loadAgentContext,
|
|
15941
15078
|
loadActiveOkfKnowledgeIndex,
|
|
15942
15079
|
listTeamRuns,
|
|
15943
15080
|
listTeamRoleBindings,
|
|
15944
15081
|
listTeamAgents,
|
|
15082
|
+
listSessionMemoryStates,
|
|
15945
15083
|
listSessionEvidenceSegments,
|
|
15946
15084
|
listSegmentEvolutionTriggers,
|
|
15085
|
+
listRegisteredProjects,
|
|
15947
15086
|
listOkfKnowledgeConcepts,
|
|
15948
15087
|
listObservabilityEvents,
|
|
15949
15088
|
listLearningReviewDecisions,
|
|
@@ -15952,6 +15091,7 @@ export {
|
|
|
15952
15091
|
listEvolutionKnowledgeReviewHistory,
|
|
15953
15092
|
listEvolutionKnowledgeRecords,
|
|
15954
15093
|
listEvolutionEvosCases,
|
|
15094
|
+
listDiscoveredProjects,
|
|
15955
15095
|
listCodeAgentTraceRefs,
|
|
15956
15096
|
lintOkfKnowledge,
|
|
15957
15097
|
lintLearningCandidates,
|
|
@@ -15973,10 +15113,8 @@ export {
|
|
|
15973
15113
|
getEnabledPluginIds,
|
|
15974
15114
|
formatWorkflowPlan,
|
|
15975
15115
|
formatWorkflowList,
|
|
15976
|
-
formatTaskContract,
|
|
15977
15116
|
formatScopedKnowledgePromptBlock,
|
|
15978
15117
|
formatRetentionDryRun,
|
|
15979
|
-
formatProjectContextPlan,
|
|
15980
15118
|
formatPackValidation,
|
|
15981
15119
|
formatPackInstallDryRun,
|
|
15982
15120
|
formatOkfKnowledgeQuery,
|
|
@@ -15998,7 +15136,6 @@ export {
|
|
|
15998
15136
|
formatCodexCapabilityVerificationArtifact,
|
|
15999
15137
|
formatAgentLoadedContextPromptBlock,
|
|
16000
15138
|
formatAgentContextDryRun,
|
|
16001
|
-
formatAgentComposeDryRun,
|
|
16002
15139
|
findCandidateConflictOrDuplicate,
|
|
16003
15140
|
exports_triggers as evolutionTriggers,
|
|
16004
15141
|
exports_review as evolutionReview,
|
|
@@ -16019,9 +15156,7 @@ export {
|
|
|
16019
15156
|
createTmuxRuntimeAdapter,
|
|
16020
15157
|
createTeamRunStore,
|
|
16021
15158
|
createTeamRoleRuntimeContext,
|
|
16022
|
-
createTaskContract,
|
|
16023
15159
|
createScopedKnowledgeContextPack,
|
|
16024
|
-
createProjectContextPlan,
|
|
16025
15160
|
createPluginRegistry,
|
|
16026
15161
|
createOkfKnowledgePlanFromDistillationBatch,
|
|
16027
15162
|
createObservabilityEvent,
|
|
@@ -16047,7 +15182,6 @@ export {
|
|
|
16047
15182
|
createCodexCapabilityVerificationArtifact,
|
|
16048
15183
|
createCodeAgentTraceRef,
|
|
16049
15184
|
createCliLogEntry,
|
|
16050
|
-
composeAgentDryRun,
|
|
16051
15185
|
cleanupDaemonState,
|
|
16052
15186
|
classifyCommandRisk,
|
|
16053
15187
|
checkProtectedZonePaths,
|
|
@@ -16070,9 +15204,9 @@ export {
|
|
|
16070
15204
|
TeamMessageBroker,
|
|
16071
15205
|
TEAM_INTERNAL_WAKE_SIGNAL,
|
|
16072
15206
|
SessionMemoryEvidenceError,
|
|
15207
|
+
ProjectRegistrationError,
|
|
16073
15208
|
PluginRegistryError,
|
|
16074
15209
|
PluginRegistry,
|
|
16075
|
-
PROJECT_CONTEXT_ALLOWED_RELATIVE_PATHS,
|
|
16076
15210
|
NodeTeamRuntimeCommandRunner,
|
|
16077
15211
|
EvoDevConfigError,
|
|
16078
15212
|
EvoDevAssetError,
|