@evo-dev/core 0.0.1-alpha.19 → 0.0.1-alpha.20
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 +59 -8
- package/dist/index.js +803 -230
- package/package.json +1 -1
- package/src/evolution/evidence/session-memory/retention.ts +359 -78
- package/src/evolution/evidence/session-memory/segment.ts +15 -1
- package/src/evolution/evidence/session-memory/storage.ts +37 -6
- package/src/evolution/evidence/session-memory/types.ts +5 -2
- package/src/evolution/evidence/session-memory/updater.ts +23 -15
- package/src/evolution/knowledge/changes.ts +6 -0
- package/src/evolution/knowledge/freshness.ts +17 -57
- package/src/evolution/knowledge/index.ts +204 -12
- package/src/evolution/knowledge/support.ts +135 -0
- package/src/hooks/index.ts +124 -20
package/dist/index.js
CHANGED
|
@@ -14,7 +14,7 @@ var __export = (target, all) => {
|
|
|
14
14
|
};
|
|
15
15
|
|
|
16
16
|
// packages/core/src/agents/index.ts
|
|
17
|
-
import { readFile as
|
|
17
|
+
import { readFile as readFile12 } from "node:fs/promises";
|
|
18
18
|
|
|
19
19
|
// packages/core/src/evolution/knowledge/index.ts
|
|
20
20
|
var exports_knowledge = {};
|
|
@@ -51,8 +51,10 @@ __export(exports_knowledge, {
|
|
|
51
51
|
readFailedOkfKnowledgePlanArtifact: () => readFailedOkfKnowledgePlanArtifact,
|
|
52
52
|
readOkfKnowledgeConcept: () => readOkfKnowledgeConcept,
|
|
53
53
|
rebuildOkfKnowledgeIndexes: () => rebuildOkfKnowledgeIndexes,
|
|
54
|
+
recordContextInjectionOutcome: () => recordContextInjectionOutcome,
|
|
54
55
|
resolveContextInjectionReceiptPath: () => resolveContextInjectionReceiptPath,
|
|
55
56
|
resolveOkfKnowledgePaths: () => resolveOkfKnowledgePaths,
|
|
57
|
+
resolveOkfKnowledgeRuntimeEligibility: () => resolveOkfKnowledgeRuntimeEligibility,
|
|
56
58
|
resumeFailedOkfKnowledgePlan: () => resumeFailedOkfKnowledgePlan,
|
|
57
59
|
revokeOkfKnowledgeConcept: () => revokeOkfKnowledgeConcept,
|
|
58
60
|
scoreLexicalKnowledgeDocument: () => scoreLexicalKnowledgeDocument,
|
|
@@ -64,7 +66,7 @@ __export(exports_knowledge, {
|
|
|
64
66
|
});
|
|
65
67
|
import { createHash as createHash5 } from "node:crypto";
|
|
66
68
|
import { existsSync, readFileSync as readFileSync2, readdirSync } from "node:fs";
|
|
67
|
-
import { mkdir as mkdir8, readFile as
|
|
69
|
+
import { mkdir as mkdir8, readFile as readFile11, readdir as readdir8, rename as rename3, rm as rm4, stat as stat5, writeFile as writeFile7 } from "node:fs/promises";
|
|
68
70
|
import { dirname as dirname9, isAbsolute as isAbsolute5, join as join13, relative as relative4, resolve as resolve4 } from "node:path";
|
|
69
71
|
|
|
70
72
|
// packages/core/src/config/paths.ts
|
|
@@ -115,7 +117,7 @@ function stripTrailingSlash(path) {
|
|
|
115
117
|
}
|
|
116
118
|
|
|
117
119
|
// packages/core/src/config/settings.ts
|
|
118
|
-
import { readFile as
|
|
120
|
+
import { readFile as readFile9 } from "node:fs/promises";
|
|
119
121
|
|
|
120
122
|
// packages/core/src/evolution/evidence/session-memory/index.ts
|
|
121
123
|
var exports_session_memory = {};
|
|
@@ -130,18 +132,23 @@ __export(exports_session_memory, {
|
|
|
130
132
|
createSemanticPacketMessageKey: () => createSemanticPacketMessageKey,
|
|
131
133
|
detectSessionMemorySensitivity: () => detectSessionMemorySensitivity,
|
|
132
134
|
inspectHistoricalSessionEvidenceRetention: () => inspectHistoricalSessionEvidenceRetention,
|
|
135
|
+
inspectSessionEvidenceRetention: () => inspectSessionEvidenceRetention,
|
|
133
136
|
listSessionEvidenceSegments: () => listSessionEvidenceSegments,
|
|
134
137
|
listSessionMemoryStates: () => listSessionMemoryStates,
|
|
135
138
|
mergeSessionMemorySensitivity: () => mergeSessionMemorySensitivity,
|
|
139
|
+
migrateLegacySessionEvidenceRetention: () => migrateLegacySessionEvidenceRetention,
|
|
136
140
|
parseSessionMemoryPolicy: () => parseSessionMemoryPolicy,
|
|
137
141
|
purgeExpiredHistoricalSessionEvidence: () => purgeExpiredHistoricalSessionEvidence,
|
|
142
|
+
purgeExpiredSessionEvidence: () => purgeExpiredSessionEvidence,
|
|
138
143
|
readLineRange: () => readLineRange,
|
|
139
144
|
readSessionCursor: () => readSessionCursor,
|
|
140
145
|
readSessionEvidenceSegment: () => readSessionEvidenceSegment,
|
|
141
146
|
readSessionState: () => readSessionState,
|
|
142
147
|
redactSessionMemoryCredentialText: () => redactSessionMemoryCredentialText,
|
|
143
148
|
redactSessionMemoryCredentials: () => redactSessionMemoryCredentials,
|
|
149
|
+
resetSessionRawEvents: () => resetSessionRawEvents,
|
|
144
150
|
resolveSessionMemoryPaths: () => resolveSessionMemoryPaths,
|
|
151
|
+
rewriteSessionRawEvents: () => rewriteSessionRawEvents,
|
|
145
152
|
updateSessionEvidenceSegmentLifecycle: () => updateSessionEvidenceSegmentLifecycle,
|
|
146
153
|
updateSessionMemoryFromHook: () => updateSessionMemoryFromHook,
|
|
147
154
|
writeHistoricalSessionEvidenceSegment: () => writeHistoricalSessionEvidenceSegment,
|
|
@@ -1936,6 +1943,9 @@ function asRecord(value) {
|
|
|
1936
1943
|
function createSemanticPacketMessageKey(value) {
|
|
1937
1944
|
return sha256Hex(value);
|
|
1938
1945
|
}
|
|
1946
|
+
// packages/core/src/evolution/evidence/session-memory/retention.ts
|
|
1947
|
+
import { readFile as readFile5 } from "node:fs/promises";
|
|
1948
|
+
|
|
1939
1949
|
// packages/core/src/evolution/candidates/index.ts
|
|
1940
1950
|
var exports_candidates = {};
|
|
1941
1951
|
__export(exports_candidates, {
|
|
@@ -2812,6 +2822,36 @@ async function appendRawEvent(path, event) {
|
|
|
2812
2822
|
`, "utf8");
|
|
2813
2823
|
return { lineNumber: existingLineCount + 1 };
|
|
2814
2824
|
}
|
|
2825
|
+
async function resetSessionRawEvents(path) {
|
|
2826
|
+
await mkdir3(dirname3(path), { recursive: true, mode: 448 });
|
|
2827
|
+
await writeFile3(path, "", { encoding: "utf8", mode: 384 });
|
|
2828
|
+
await chmod(path, 384);
|
|
2829
|
+
}
|
|
2830
|
+
async function rewriteSessionRawEvents(path, lines, expectedCurrent) {
|
|
2831
|
+
await ensurePrivateSessionMemoryDirectory(dirname3(path));
|
|
2832
|
+
const temporaryPath = join6(dirname3(path), `.${randomUUID()}.tmp`);
|
|
2833
|
+
try {
|
|
2834
|
+
const handle = await open(temporaryPath, "wx", 384);
|
|
2835
|
+
try {
|
|
2836
|
+
await handle.writeFile(lines.length === 0 ? "" : `${lines.join(`
|
|
2837
|
+
`)}
|
|
2838
|
+
`, "utf8");
|
|
2839
|
+
await handle.sync();
|
|
2840
|
+
} finally {
|
|
2841
|
+
await handle.close();
|
|
2842
|
+
}
|
|
2843
|
+
const current = await readFile4(path, "utf8").catch(() => null);
|
|
2844
|
+
if (current !== expectedCurrent)
|
|
2845
|
+
return false;
|
|
2846
|
+
await rename(temporaryPath, path);
|
|
2847
|
+
await chmod(path, 384);
|
|
2848
|
+
return true;
|
|
2849
|
+
} finally {
|
|
2850
|
+
await rm2(temporaryPath, { force: true }).catch(() => {
|
|
2851
|
+
return;
|
|
2852
|
+
});
|
|
2853
|
+
}
|
|
2854
|
+
}
|
|
2815
2855
|
async function readLineRange(path, fromLine, toLine) {
|
|
2816
2856
|
const text2 = await readFile4(path, "utf8");
|
|
2817
2857
|
const lines = text2.split(`
|
|
@@ -2967,7 +3007,7 @@ async function writeHistoricalSessionStateAndIndex(input) {
|
|
|
2967
3007
|
return { state, segmentIds: ordered.map((segment) => segment.id) };
|
|
2968
3008
|
}
|
|
2969
3009
|
async function writeSessionEvidenceSegmentAtomic(input) {
|
|
2970
|
-
|
|
3010
|
+
assertSessionEvidenceRetentionState(input.segment);
|
|
2971
3011
|
const paths = resolveSessionMemoryPaths({
|
|
2972
3012
|
homeDir: input.homeDir,
|
|
2973
3013
|
projectKey: input.segment.projectKey,
|
|
@@ -2975,18 +3015,18 @@ async function writeSessionEvidenceSegmentAtomic(input) {
|
|
|
2975
3015
|
});
|
|
2976
3016
|
await writePrivateAtomicJson(paths.segmentPath(input.segment.id), input.segment);
|
|
2977
3017
|
}
|
|
2978
|
-
function
|
|
2979
|
-
if (segment.
|
|
2980
|
-
throw new Error("Atomic Session Evidence rewrite
|
|
3018
|
+
function assertSessionEvidenceRetentionState(segment) {
|
|
3019
|
+
if (segment.retention === undefined) {
|
|
3020
|
+
throw new Error("Atomic Session Evidence rewrite requires retention metadata.");
|
|
2981
3021
|
}
|
|
2982
3022
|
if (segment.retention.rawState === "available") {
|
|
2983
3023
|
if (!segment.rawExcerpt.stored || segment.rawExcerpt.byteLength !== Buffer.byteLength(segment.rawExcerpt.content, "utf8") || segment.rawExcerpt.sha256 !== sha256Hex(segment.rawExcerpt.content) || segment.retention.originalRawSha256 !== segment.rawExcerpt.sha256) {
|
|
2984
|
-
throw new Error("Available
|
|
3024
|
+
throw new Error("Available Session Evidence raw state is invalid.");
|
|
2985
3025
|
}
|
|
2986
3026
|
return;
|
|
2987
3027
|
}
|
|
2988
3028
|
if (segment.rawExcerpt.stored || segment.rawExcerpt.content !== "" || segment.rawExcerpt.byteLength !== 0 || segment.rawExcerpt.sha256 !== sha256Hex("") || segment.retention.rawPurgedAt === null || segment.lifecycle.status !== "raw-expired") {
|
|
2989
|
-
throw new Error("Purged
|
|
3029
|
+
throw new Error("Purged Session Evidence raw state is invalid.");
|
|
2990
3030
|
}
|
|
2991
3031
|
}
|
|
2992
3032
|
async function writeJson(path, value) {
|
|
@@ -3203,69 +3243,95 @@ async function listJsonFiles(path) {
|
|
|
3203
3243
|
|
|
3204
3244
|
// packages/core/src/evolution/evidence/session-memory/retention.ts
|
|
3205
3245
|
var DEFAULT_RETENTION_LIMIT = 20;
|
|
3206
|
-
var MAX_RETENTION_LIMIT =
|
|
3246
|
+
var MAX_RETENTION_LIMIT = 1e4;
|
|
3247
|
+
var DEFAULT_LEGACY_RETENTION_DAYS = 30;
|
|
3248
|
+
async function inspectSessionEvidenceRetention(input) {
|
|
3249
|
+
const result = await inspectRetention(input, () => true);
|
|
3250
|
+
const expiredEvents = await expireCapturedRawEvents({ ...input, apply: false });
|
|
3251
|
+
result.expiredCapturedEventPayloads = expiredEvents.payloads;
|
|
3252
|
+
result.expiredCapturedEventRawBytes = expiredEvents.rawBytes;
|
|
3253
|
+
return result;
|
|
3254
|
+
}
|
|
3207
3255
|
async function inspectHistoricalSessionEvidenceRetention(input) {
|
|
3256
|
+
return inspectRetention(input, (segment) => segment.origin?.kind === "historical-import");
|
|
3257
|
+
}
|
|
3258
|
+
async function inspectRetention(input, include) {
|
|
3208
3259
|
const now = normalizeTimestamp(input.now);
|
|
3209
|
-
const
|
|
3260
|
+
const segments = (await listSessionEvidenceSegments({
|
|
3210
3261
|
homeDir: input.homeDir,
|
|
3211
3262
|
projectKey: input.projectKey
|
|
3212
|
-
})).filter(
|
|
3213
|
-
const
|
|
3214
|
-
|
|
3215
|
-
|
|
3216
|
-
|
|
3217
|
-
|
|
3218
|
-
|
|
3219
|
-
|
|
3220
|
-
|
|
3221
|
-
|
|
3222
|
-
|
|
3223
|
-
|
|
3263
|
+
})).filter(include);
|
|
3264
|
+
const retentionDays = await loadLegacyRetentionDays(input.homeDir, input.projectKey);
|
|
3265
|
+
const result = emptyInspection();
|
|
3266
|
+
const context = createRetentionContext();
|
|
3267
|
+
for (const segment of segments) {
|
|
3268
|
+
if (segment.retention === undefined) {
|
|
3269
|
+
const rawBytes = segment.rawExcerpt.stored ? segment.rawExcerpt.byteLength : 0;
|
|
3270
|
+
result.legacyWithoutRetention += 1;
|
|
3271
|
+
result.legacyRawBytes += rawBytes;
|
|
3272
|
+
const expiresAt = legacyExpiresAt(segment, retentionDays);
|
|
3273
|
+
if (Date.parse(expiresAt) <= Date.parse(now)) {
|
|
3274
|
+
result.legacyOverdue += 1;
|
|
3275
|
+
result.legacyOverdueRawBytes += rawBytes;
|
|
3276
|
+
const protection2 = await findRetentionProtection({
|
|
3277
|
+
homeDir: input.homeDir,
|
|
3278
|
+
segment,
|
|
3279
|
+
context
|
|
3280
|
+
});
|
|
3281
|
+
if (protection2 === null)
|
|
3282
|
+
result.purgeEligible += 1;
|
|
3283
|
+
else
|
|
3284
|
+
addProtection(result, protection2);
|
|
3285
|
+
}
|
|
3286
|
+
continue;
|
|
3287
|
+
}
|
|
3288
|
+
if (segment.retention.rawState === "purged") {
|
|
3224
3289
|
result.purged += 1;
|
|
3225
3290
|
continue;
|
|
3226
3291
|
}
|
|
3227
3292
|
result.available += 1;
|
|
3228
3293
|
if (!hasValidAvailableRetention(segment)) {
|
|
3229
3294
|
result.overdue += 1;
|
|
3230
|
-
result
|
|
3231
|
-
result.protectionReasons["retention-invalid"] = (result.protectionReasons["retention-invalid"] ?? 0) + 1;
|
|
3295
|
+
addProtection(result, "retention-invalid");
|
|
3232
3296
|
continue;
|
|
3233
3297
|
}
|
|
3234
|
-
if (
|
|
3298
|
+
if (Date.parse(segment.retention.expiresAt) > Date.parse(now))
|
|
3235
3299
|
continue;
|
|
3236
|
-
}
|
|
3237
3300
|
result.overdue += 1;
|
|
3238
3301
|
const protection = await findRetentionProtection({
|
|
3239
3302
|
homeDir: input.homeDir,
|
|
3240
3303
|
segment,
|
|
3241
|
-
|
|
3304
|
+
context
|
|
3242
3305
|
});
|
|
3243
|
-
if (protection === null)
|
|
3306
|
+
if (protection === null)
|
|
3244
3307
|
result.purgeEligible += 1;
|
|
3245
|
-
|
|
3246
|
-
|
|
3247
|
-
result.protected += 1;
|
|
3248
|
-
result.protectionReasons[protection] = (result.protectionReasons[protection] ?? 0) + 1;
|
|
3308
|
+
else
|
|
3309
|
+
addProtection(result, protection);
|
|
3249
3310
|
}
|
|
3250
3311
|
return result;
|
|
3251
3312
|
}
|
|
3313
|
+
async function purgeExpiredSessionEvidence(input) {
|
|
3314
|
+
return purgeRetention(input, () => true);
|
|
3315
|
+
}
|
|
3252
3316
|
async function purgeExpiredHistoricalSessionEvidence(input) {
|
|
3317
|
+
return purgeRetention(input, (segment) => segment.origin?.kind === "historical-import");
|
|
3318
|
+
}
|
|
3319
|
+
async function purgeRetention(input, include) {
|
|
3253
3320
|
const now = normalizeTimestamp(input.now);
|
|
3254
3321
|
const limit = normalizeLimit(input.limit);
|
|
3255
|
-
const
|
|
3322
|
+
const segments = (await listSessionEvidenceSegments({
|
|
3256
3323
|
homeDir: input.homeDir,
|
|
3257
3324
|
projectKey: input.projectKey
|
|
3258
|
-
})).filter((segment) => segment
|
|
3325
|
+
})).filter((segment) => include(segment) && segment.retention !== undefined).sort((left, right) => (left.retention?.expiresAt ?? "").localeCompare(right.retention?.expiresAt ?? "") || left.id.localeCompare(right.id));
|
|
3259
3326
|
const result = {
|
|
3260
3327
|
scanned: 0,
|
|
3261
3328
|
due: 0,
|
|
3262
3329
|
purged: 0,
|
|
3263
|
-
alreadyPurged:
|
|
3330
|
+
alreadyPurged: segments.filter((segment) => segment.retention?.rawState === "purged").length,
|
|
3264
3331
|
protected: []
|
|
3265
3332
|
};
|
|
3266
|
-
const
|
|
3267
|
-
|
|
3268
|
-
for (const segment of historical) {
|
|
3333
|
+
const context = createRetentionContext();
|
|
3334
|
+
for (const segment of segments) {
|
|
3269
3335
|
if (result.scanned >= limit)
|
|
3270
3336
|
break;
|
|
3271
3337
|
if (segment.retention === undefined || segment.retention.rawState === "purged")
|
|
@@ -3286,7 +3352,7 @@ async function purgeExpiredHistoricalSessionEvidence(input) {
|
|
|
3286
3352
|
const protection = await findRetentionProtection({
|
|
3287
3353
|
homeDir: input.homeDir,
|
|
3288
3354
|
segment,
|
|
3289
|
-
|
|
3355
|
+
context
|
|
3290
3356
|
});
|
|
3291
3357
|
if (protection !== null) {
|
|
3292
3358
|
result.protected.push({
|
|
@@ -3302,8 +3368,7 @@ async function purgeExpiredHistoricalSessionEvidence(input) {
|
|
|
3302
3368
|
sessionKey: segment.sessionKey,
|
|
3303
3369
|
segmentId: segment.id
|
|
3304
3370
|
});
|
|
3305
|
-
|
|
3306
|
-
if (!hasValidAvailableRetention(current) || current.retention?.rawState !== "available" || current.retention.expiresAt !== segment.retention.expiresAt || !current.rawExcerpt.stored || current.rawExcerpt.sha256 !== segment.rawExcerpt.sha256 || current.lifecycle.reviewState !== segment.lifecycle.reviewState || currentTrigger === null || !isTerminalTrigger(currentTrigger)) {
|
|
3371
|
+
if (!hasValidAvailableRetention(current) || current.retention?.rawState !== "available" || current.retention.expiresAt !== segment.retention.expiresAt || !current.rawExcerpt.stored || current.rawExcerpt.sha256 !== segment.rawExcerpt.sha256 || current.lifecycle.reviewState !== segment.lifecycle.reviewState) {
|
|
3307
3372
|
result.protected.push({
|
|
3308
3373
|
projectKey: segment.projectKey,
|
|
3309
3374
|
segmentId: segment.id,
|
|
@@ -3311,30 +3376,115 @@ async function purgeExpiredHistoricalSessionEvidence(input) {
|
|
|
3311
3376
|
});
|
|
3312
3377
|
continue;
|
|
3313
3378
|
}
|
|
3314
|
-
const
|
|
3379
|
+
const freshProtection = await findRetentionProtection({
|
|
3315
3380
|
homeDir: input.homeDir,
|
|
3316
|
-
|
|
3381
|
+
segment: current,
|
|
3382
|
+
context: createRetentionContext()
|
|
3317
3383
|
});
|
|
3318
|
-
if (
|
|
3384
|
+
if (freshProtection !== null) {
|
|
3319
3385
|
result.protected.push({
|
|
3320
|
-
projectKey:
|
|
3321
|
-
segmentId:
|
|
3322
|
-
reason:
|
|
3386
|
+
projectKey: current.projectKey,
|
|
3387
|
+
segmentId: current.id,
|
|
3388
|
+
reason: freshProtection
|
|
3323
3389
|
});
|
|
3324
3390
|
continue;
|
|
3325
3391
|
}
|
|
3326
3392
|
await writeSessionEvidenceSegmentAtomic({
|
|
3327
3393
|
homeDir: input.homeDir,
|
|
3328
|
-
segment:
|
|
3394
|
+
segment: createPurgedSegment(current, now)
|
|
3329
3395
|
});
|
|
3330
3396
|
result.purged += 1;
|
|
3331
3397
|
}
|
|
3332
3398
|
return result;
|
|
3333
3399
|
}
|
|
3400
|
+
async function migrateLegacySessionEvidenceRetention(input) {
|
|
3401
|
+
const now = normalizeTimestamp(input.now);
|
|
3402
|
+
const retentionDays = await loadLegacyRetentionDays(input.homeDir, input.projectKey);
|
|
3403
|
+
const segments = await listSessionEvidenceSegments({
|
|
3404
|
+
homeDir: input.homeDir,
|
|
3405
|
+
projectKey: input.projectKey
|
|
3406
|
+
});
|
|
3407
|
+
let migrated = 0;
|
|
3408
|
+
const context = createRetentionContext();
|
|
3409
|
+
for (const segment of segments) {
|
|
3410
|
+
if (segment.retention !== undefined || !segment.rawExcerpt.stored)
|
|
3411
|
+
continue;
|
|
3412
|
+
const policyDays = retentionDays.get(sessionKey(segment)) ?? DEFAULT_LEGACY_RETENTION_DAYS;
|
|
3413
|
+
if (Date.parse(addDays(segment.createdAt, policyDays)) > Date.parse(now))
|
|
3414
|
+
continue;
|
|
3415
|
+
const protection = await findRetentionProtection({
|
|
3416
|
+
homeDir: input.homeDir,
|
|
3417
|
+
segment,
|
|
3418
|
+
context
|
|
3419
|
+
});
|
|
3420
|
+
if (protection !== null)
|
|
3421
|
+
continue;
|
|
3422
|
+
const current = await readSessionEvidenceSegment({
|
|
3423
|
+
homeDir: input.homeDir,
|
|
3424
|
+
projectKey: segment.projectKey,
|
|
3425
|
+
sessionKey: segment.sessionKey,
|
|
3426
|
+
segmentId: segment.id
|
|
3427
|
+
});
|
|
3428
|
+
if (current.retention !== undefined || JSON.stringify(current) !== JSON.stringify(segment)) {
|
|
3429
|
+
continue;
|
|
3430
|
+
}
|
|
3431
|
+
await writeSessionEvidenceSegmentAtomic({
|
|
3432
|
+
homeDir: input.homeDir,
|
|
3433
|
+
segment: {
|
|
3434
|
+
...current,
|
|
3435
|
+
retention: {
|
|
3436
|
+
policyDays,
|
|
3437
|
+
expiresAt: addDays(current.createdAt, policyDays),
|
|
3438
|
+
rawState: "available",
|
|
3439
|
+
rawPurgedAt: null,
|
|
3440
|
+
originalRawSha256: current.rawExcerpt.sha256
|
|
3441
|
+
}
|
|
3442
|
+
}
|
|
3443
|
+
});
|
|
3444
|
+
migrated += 1;
|
|
3445
|
+
}
|
|
3446
|
+
const expiredEvents = await expireCapturedRawEvents({
|
|
3447
|
+
homeDir: input.homeDir,
|
|
3448
|
+
projectKey: input.projectKey,
|
|
3449
|
+
now,
|
|
3450
|
+
apply: true
|
|
3451
|
+
});
|
|
3452
|
+
return {
|
|
3453
|
+
migrated,
|
|
3454
|
+
expiredCapturedEventPayloads: expiredEvents.payloads,
|
|
3455
|
+
expiredCapturedEventRawBytes: expiredEvents.rawBytes,
|
|
3456
|
+
purge: await purgeExpiredSessionEvidence({
|
|
3457
|
+
homeDir: input.homeDir,
|
|
3458
|
+
projectKey: input.projectKey,
|
|
3459
|
+
now,
|
|
3460
|
+
limit: Math.max(DEFAULT_RETENTION_LIMIT, segments.length)
|
|
3461
|
+
})
|
|
3462
|
+
};
|
|
3463
|
+
}
|
|
3464
|
+
function emptyInspection() {
|
|
3465
|
+
return {
|
|
3466
|
+
available: 0,
|
|
3467
|
+
purged: 0,
|
|
3468
|
+
overdue: 0,
|
|
3469
|
+
purgeEligible: 0,
|
|
3470
|
+
protected: 0,
|
|
3471
|
+
legacyWithoutRetention: 0,
|
|
3472
|
+
legacyOverdue: 0,
|
|
3473
|
+
legacyRawBytes: 0,
|
|
3474
|
+
legacyOverdueRawBytes: 0,
|
|
3475
|
+
expiredCapturedEventPayloads: 0,
|
|
3476
|
+
expiredCapturedEventRawBytes: 0,
|
|
3477
|
+
protectionReasons: {}
|
|
3478
|
+
};
|
|
3479
|
+
}
|
|
3480
|
+
function addProtection(result, reason) {
|
|
3481
|
+
result.protected += 1;
|
|
3482
|
+
result.protectionReasons[reason] = (result.protectionReasons[reason] ?? 0) + 1;
|
|
3483
|
+
}
|
|
3334
3484
|
function normalizeLimit(value) {
|
|
3335
3485
|
const limit = value ?? DEFAULT_RETENTION_LIMIT;
|
|
3336
3486
|
if (!Number.isSafeInteger(limit) || limit < 1 || limit > MAX_RETENTION_LIMIT) {
|
|
3337
|
-
throw new Error(`
|
|
3487
|
+
throw new Error(`Session Evidence retention limit must be between 1 and ${MAX_RETENTION_LIMIT}.`);
|
|
3338
3488
|
}
|
|
3339
3489
|
return limit;
|
|
3340
3490
|
}
|
|
@@ -3346,26 +3496,38 @@ async function findRetentionProtection(input) {
|
|
|
3346
3496
|
if (input.segment.lifecycle.reviewState === "unreviewed" || input.segment.lifecycle.reviewState === "deferred") {
|
|
3347
3497
|
return "segment-review-pending";
|
|
3348
3498
|
}
|
|
3349
|
-
const trigger = await findSegmentTrigger(input.homeDir, input.segment);
|
|
3350
|
-
if (trigger === null)
|
|
3499
|
+
const trigger = await findSegmentTrigger(input.homeDir, input.segment, input.context);
|
|
3500
|
+
if (trigger === null) {
|
|
3501
|
+
if (input.segment.reason === "session-memory-threshold")
|
|
3502
|
+
return null;
|
|
3351
3503
|
return "trigger-missing";
|
|
3504
|
+
}
|
|
3352
3505
|
if (!isTerminalTrigger(trigger))
|
|
3353
3506
|
return "trigger-not-terminal";
|
|
3354
|
-
let snapshot = input.reviewSnapshots.get(input.segment.projectKey);
|
|
3507
|
+
let snapshot = input.context.reviewSnapshots.get(input.segment.projectKey);
|
|
3355
3508
|
if (snapshot === undefined) {
|
|
3356
3509
|
snapshot = readEvolutionReviewSnapshot({
|
|
3357
3510
|
homeDir: input.homeDir,
|
|
3358
3511
|
projectKey: input.segment.projectKey
|
|
3359
3512
|
});
|
|
3360
|
-
input.reviewSnapshots.set(input.segment.projectKey, snapshot);
|
|
3513
|
+
input.context.reviewSnapshots.set(input.segment.projectKey, snapshot);
|
|
3361
3514
|
}
|
|
3362
3515
|
return hasPendingDerivedReview(await snapshot, input.segment) ? "derived-review-pending" : null;
|
|
3363
3516
|
}
|
|
3364
|
-
async function findSegmentTrigger(homeDir, segment) {
|
|
3365
|
-
|
|
3366
|
-
|
|
3367
|
-
projectKey: segment.projectKey
|
|
3368
|
-
|
|
3517
|
+
async function findSegmentTrigger(homeDir, segment, context) {
|
|
3518
|
+
let triggerMap = context.triggerMaps.get(segment.projectKey);
|
|
3519
|
+
if (triggerMap === undefined) {
|
|
3520
|
+
triggerMap = listSegmentEvolutionTriggers({ homeDir, projectKey: segment.projectKey }).then((triggers) => {
|
|
3521
|
+
const map = new Map;
|
|
3522
|
+
for (const trigger of triggers) {
|
|
3523
|
+
const key = `${trigger.sessionKey}\x00${trigger.segmentId}`;
|
|
3524
|
+
map.set(key, [...map.get(key) ?? [], trigger]);
|
|
3525
|
+
}
|
|
3526
|
+
return map;
|
|
3527
|
+
});
|
|
3528
|
+
context.triggerMaps.set(segment.projectKey, triggerMap);
|
|
3529
|
+
}
|
|
3530
|
+
const matches = (await triggerMap).get(`${segment.sessionKey}\x00${segment.id}`) ?? [];
|
|
3369
3531
|
return matches.length === 1 ? matches[0] ?? null : null;
|
|
3370
3532
|
}
|
|
3371
3533
|
function isTerminalTrigger(trigger) {
|
|
@@ -3384,10 +3546,10 @@ function hasPendingDerivedReview(snapshot, segment) {
|
|
|
3384
3546
|
}
|
|
3385
3547
|
return snapshot.reviewCandidates.some((candidate) => candidate.runId === runId && candidate.reviewState !== "accepted" && candidate.reviewState !== "rejected");
|
|
3386
3548
|
}
|
|
3387
|
-
function
|
|
3549
|
+
function createPurgedSegment(segment, now) {
|
|
3388
3550
|
const retention = segment.retention;
|
|
3389
3551
|
if (retention === undefined || retention.rawState !== "available") {
|
|
3390
|
-
throw new Error("
|
|
3552
|
+
throw new Error("Session Evidence raw body is not available for purge.");
|
|
3391
3553
|
}
|
|
3392
3554
|
return {
|
|
3393
3555
|
...segment,
|
|
@@ -3415,11 +3577,109 @@ function createPurgedHistoricalSegment(segment, now) {
|
|
|
3415
3577
|
}
|
|
3416
3578
|
};
|
|
3417
3579
|
}
|
|
3580
|
+
function createRetentionContext() {
|
|
3581
|
+
return {
|
|
3582
|
+
reviewSnapshots: new Map,
|
|
3583
|
+
triggerMaps: new Map
|
|
3584
|
+
};
|
|
3585
|
+
}
|
|
3586
|
+
async function loadLegacyRetentionDays(homeDir, projectKey) {
|
|
3587
|
+
const states = await listSessionMemoryStates({ homeDir, projectKey });
|
|
3588
|
+
return new Map(states.map((state) => [`${state.projectKey}\x00${state.sessionKey}`, state.policy.retentionDays]));
|
|
3589
|
+
}
|
|
3590
|
+
function legacyExpiresAt(segment, retentionDays) {
|
|
3591
|
+
return addDays(segment.createdAt, retentionDays.get(sessionKey(segment)) ?? DEFAULT_LEGACY_RETENTION_DAYS);
|
|
3592
|
+
}
|
|
3593
|
+
function sessionKey(segment) {
|
|
3594
|
+
return `${segment.projectKey}\x00${segment.sessionKey}`;
|
|
3595
|
+
}
|
|
3596
|
+
function addDays(value, days) {
|
|
3597
|
+
const timestamp = Date.parse(value);
|
|
3598
|
+
if (!Number.isFinite(timestamp))
|
|
3599
|
+
throw new Error("Invalid Session Evidence retention timestamp.");
|
|
3600
|
+
return new Date(timestamp + days * 24 * 60 * 60 * 1000).toISOString();
|
|
3601
|
+
}
|
|
3602
|
+
async function expireCapturedRawEvents(input) {
|
|
3603
|
+
const now = Date.parse(normalizeTimestamp(input.now));
|
|
3604
|
+
const states = await listSessionMemoryStates({
|
|
3605
|
+
homeDir: input.homeDir,
|
|
3606
|
+
projectKey: input.projectKey
|
|
3607
|
+
});
|
|
3608
|
+
let payloads = 0;
|
|
3609
|
+
let rawBytes = 0;
|
|
3610
|
+
for (const state of states) {
|
|
3611
|
+
const stateUpdatedAt = Date.parse(state.updatedAt);
|
|
3612
|
+
if (Number.isFinite(stateUpdatedAt) && now - stateUpdatedAt < 5 * 60 * 1000)
|
|
3613
|
+
continue;
|
|
3614
|
+
const paths = resolveSessionMemoryPaths({
|
|
3615
|
+
homeDir: input.homeDir,
|
|
3616
|
+
projectKey: state.projectKey,
|
|
3617
|
+
sessionKey: state.sessionKey
|
|
3618
|
+
});
|
|
3619
|
+
const cursor = await readCursor(paths.cursorPath);
|
|
3620
|
+
const capturedLine = cursor?.lastCapturedLine ?? 0;
|
|
3621
|
+
if (capturedLine <= 0)
|
|
3622
|
+
continue;
|
|
3623
|
+
let text2;
|
|
3624
|
+
try {
|
|
3625
|
+
text2 = await readFile5(paths.eventsPath, "utf8");
|
|
3626
|
+
} catch {
|
|
3627
|
+
continue;
|
|
3628
|
+
}
|
|
3629
|
+
const lines = text2.split(`
|
|
3630
|
+
`).filter((line) => line.trim() !== "");
|
|
3631
|
+
let changed = false;
|
|
3632
|
+
let filePayloads = 0;
|
|
3633
|
+
let fileRawBytes = 0;
|
|
3634
|
+
const nextLines = lines.map((line, index) => {
|
|
3635
|
+
if (index >= capturedLine)
|
|
3636
|
+
return line;
|
|
3637
|
+
let event;
|
|
3638
|
+
try {
|
|
3639
|
+
event = JSON.parse(line);
|
|
3640
|
+
} catch {
|
|
3641
|
+
return line;
|
|
3642
|
+
}
|
|
3643
|
+
const expiresAt = Date.parse(event.receivedAt) + state.policy.retentionDays * 24 * 60 * 60 * 1000;
|
|
3644
|
+
if (!Number.isFinite(expiresAt) || expiresAt > now || event.rawPayloadExpired === true || event.rawPayloadJson === "{}") {
|
|
3645
|
+
return line;
|
|
3646
|
+
}
|
|
3647
|
+
filePayloads += 1;
|
|
3648
|
+
fileRawBytes += Buffer.byteLength(event.rawPayloadJson, "utf8");
|
|
3649
|
+
if (!input.apply)
|
|
3650
|
+
return line;
|
|
3651
|
+
changed = true;
|
|
3652
|
+
return JSON.stringify({
|
|
3653
|
+
...event,
|
|
3654
|
+
rawPayloadJson: "{}",
|
|
3655
|
+
rawPayloadTruncated: false,
|
|
3656
|
+
rawPayloadExpired: true
|
|
3657
|
+
});
|
|
3658
|
+
});
|
|
3659
|
+
if (!input.apply) {
|
|
3660
|
+
payloads += filePayloads;
|
|
3661
|
+
rawBytes += fileRawBytes;
|
|
3662
|
+
continue;
|
|
3663
|
+
}
|
|
3664
|
+
if (changed && await rewriteSessionRawEvents(paths.eventsPath, nextLines, text2)) {
|
|
3665
|
+
payloads += filePayloads;
|
|
3666
|
+
rawBytes += fileRawBytes;
|
|
3667
|
+
}
|
|
3668
|
+
}
|
|
3669
|
+
return { payloads, rawBytes };
|
|
3670
|
+
}
|
|
3671
|
+
async function readCursor(path) {
|
|
3672
|
+
try {
|
|
3673
|
+
return JSON.parse(await readFile5(path, "utf8"));
|
|
3674
|
+
} catch {
|
|
3675
|
+
return null;
|
|
3676
|
+
}
|
|
3677
|
+
}
|
|
3418
3678
|
// packages/core/src/evolution/evidence/session-memory/updater.ts
|
|
3419
3679
|
import { join as join9 } from "node:path";
|
|
3420
3680
|
|
|
3421
3681
|
// packages/core/src/projects/index.ts
|
|
3422
|
-
import { lstat as lstat2, readFile as
|
|
3682
|
+
import { lstat as lstat2, readFile as readFile6, readdir as readdir5, realpath, stat as stat2 } from "node:fs/promises";
|
|
3423
3683
|
import { basename as basename2, dirname as dirname5, isAbsolute as isAbsolute3, join as join8, parse } from "node:path";
|
|
3424
3684
|
|
|
3425
3685
|
// packages/core/src/runtime-logs/index.ts
|
|
@@ -3511,14 +3771,14 @@ function createDebugLogEntry(input) {
|
|
|
3511
3771
|
function createTraceLogEntry(input) {
|
|
3512
3772
|
const timestamp = normalizeTimestamp(input.timestamp);
|
|
3513
3773
|
const payload = input.payload ?? null;
|
|
3514
|
-
const
|
|
3774
|
+
const sessionKey2 = input.sessionKey ?? resolveTraceSessionKey(payload);
|
|
3515
3775
|
return {
|
|
3516
3776
|
version: 1,
|
|
3517
3777
|
kind: "trace",
|
|
3518
3778
|
timestamp,
|
|
3519
3779
|
date: formatLocalDateKey(timestamp),
|
|
3520
3780
|
phase: input.phase,
|
|
3521
|
-
sessionKey,
|
|
3781
|
+
sessionKey: sessionKey2,
|
|
3522
3782
|
target: truncateString(input.target),
|
|
3523
3783
|
runtime: {
|
|
3524
3784
|
surface: input.runtime.surface,
|
|
@@ -3541,7 +3801,7 @@ function createTraceLogEntry(input) {
|
|
|
3541
3801
|
}
|
|
3542
3802
|
function createEvoDevExecutionEvent(input) {
|
|
3543
3803
|
const timestamp = normalizeTimestamp(input.timestamp);
|
|
3544
|
-
const
|
|
3804
|
+
const sessionKey2 = sanitizePersistentIdentifier(input.sessionKey, "session");
|
|
3545
3805
|
const target = normalizeExecutionEventTarget(input.target);
|
|
3546
3806
|
const eventType = sanitizeEventText(input.eventType, "unknown");
|
|
3547
3807
|
const projectKey = input.projectKey === undefined || input.projectKey === null ? null : sanitizePersistentIdentifier(input.projectKey, "project");
|
|
@@ -3562,7 +3822,7 @@ function createEvoDevExecutionEvent(input) {
|
|
|
3562
3822
|
timestamp,
|
|
3563
3823
|
target,
|
|
3564
3824
|
eventType,
|
|
3565
|
-
sessionKey,
|
|
3825
|
+
sessionKey: sessionKey2,
|
|
3566
3826
|
projectKey,
|
|
3567
3827
|
runId,
|
|
3568
3828
|
roleId,
|
|
@@ -3578,7 +3838,7 @@ function createEvoDevExecutionEvent(input) {
|
|
|
3578
3838
|
runId,
|
|
3579
3839
|
roleId,
|
|
3580
3840
|
taskId,
|
|
3581
|
-
sessionKey,
|
|
3841
|
+
sessionKey: sessionKey2,
|
|
3582
3842
|
summary,
|
|
3583
3843
|
metadata,
|
|
3584
3844
|
decision,
|
|
@@ -4415,7 +4675,7 @@ async function findNearestGitRoot(cwd) {
|
|
|
4415
4675
|
}
|
|
4416
4676
|
async function readDiscoveredProject(path) {
|
|
4417
4677
|
try {
|
|
4418
|
-
return parseDiscoveredProject(JSON.parse(await
|
|
4678
|
+
return parseDiscoveredProject(JSON.parse(await readFile6(path, "utf8")));
|
|
4419
4679
|
} catch (error) {
|
|
4420
4680
|
if (isNotFoundError(error) || error instanceof ProjectRegistrationError)
|
|
4421
4681
|
return null;
|
|
@@ -4424,7 +4684,7 @@ async function readDiscoveredProject(path) {
|
|
|
4424
4684
|
}
|
|
4425
4685
|
async function readRegisteredProject(path) {
|
|
4426
4686
|
try {
|
|
4427
|
-
return parseRegisteredProject(JSON.parse(await
|
|
4687
|
+
return parseRegisteredProject(JSON.parse(await readFile6(path, "utf8")));
|
|
4428
4688
|
} catch (error) {
|
|
4429
4689
|
if (isNotFoundError(error) || error instanceof ProjectRegistrationError)
|
|
4430
4690
|
return null;
|
|
@@ -4433,7 +4693,7 @@ async function readRegisteredProject(path) {
|
|
|
4433
4693
|
}
|
|
4434
4694
|
async function readProjectWorkspace(path) {
|
|
4435
4695
|
try {
|
|
4436
|
-
return parseProjectWorkspace(JSON.parse(await
|
|
4696
|
+
return parseProjectWorkspace(JSON.parse(await readFile6(path, "utf8")));
|
|
4437
4697
|
} catch (error) {
|
|
4438
4698
|
if (isNotFoundError(error) || error instanceof ProjectRegistrationError)
|
|
4439
4699
|
return null;
|
|
@@ -4442,7 +4702,7 @@ async function readProjectWorkspace(path) {
|
|
|
4442
4702
|
}
|
|
4443
4703
|
async function readProjectAlias(path) {
|
|
4444
4704
|
try {
|
|
4445
|
-
return parseProjectAlias(JSON.parse(await
|
|
4705
|
+
return parseProjectAlias(JSON.parse(await readFile6(path, "utf8")));
|
|
4446
4706
|
} catch (error) {
|
|
4447
4707
|
if (isNotFoundError(error) || error instanceof ProjectRegistrationError)
|
|
4448
4708
|
return null;
|
|
@@ -4521,7 +4781,7 @@ async function listLegacyHookBindings(homeDir) {
|
|
|
4521
4781
|
for (const root of roots) {
|
|
4522
4782
|
for (const sessionDir of await listDirectoryNames3(root)) {
|
|
4523
4783
|
try {
|
|
4524
|
-
const value = JSON.parse(await
|
|
4784
|
+
const value = JSON.parse(await readFile6(join8(root, sessionDir, "binding.json"), "utf8"));
|
|
4525
4785
|
if (value === null || value.target !== "claude" && value.target !== "codex" || !isNonEmptyString(value.sessionKey) || !(value.cwd === null || isAbsoluteString(value.cwd)) || !isNonEmptyString(value.updatedAt)) {
|
|
4526
4786
|
continue;
|
|
4527
4787
|
}
|
|
@@ -4642,6 +4902,7 @@ async function createSegment(input) {
|
|
|
4642
4902
|
String(toLine),
|
|
4643
4903
|
input.signals.at(-1)?.eventId ?? input.now
|
|
4644
4904
|
]);
|
|
4905
|
+
const rawSha256 = sha256Hex(truncated.content);
|
|
4645
4906
|
return {
|
|
4646
4907
|
schemaVersion: 1,
|
|
4647
4908
|
kind: "session-evidence-segment",
|
|
@@ -4654,6 +4915,13 @@ async function createSegment(input) {
|
|
|
4654
4915
|
createdAt: input.now,
|
|
4655
4916
|
reason: input.reason,
|
|
4656
4917
|
strength: input.strength,
|
|
4918
|
+
retention: {
|
|
4919
|
+
policyDays: input.state.policy.retentionDays,
|
|
4920
|
+
expiresAt: addDays2(input.now, input.state.policy.retentionDays),
|
|
4921
|
+
rawState: "available",
|
|
4922
|
+
rawPurgedAt: null,
|
|
4923
|
+
originalRawSha256: rawSha256
|
|
4924
|
+
},
|
|
4657
4925
|
source: {
|
|
4658
4926
|
traceRefId: input.cursor.traceRefId,
|
|
4659
4927
|
sourcePath: input.paths.eventsPath,
|
|
@@ -4671,7 +4939,7 @@ async function createSegment(input) {
|
|
|
4671
4939
|
content: truncated.content,
|
|
4672
4940
|
truncated: raw.truncated || truncated.truncated,
|
|
4673
4941
|
byteLength: Buffer.byteLength(truncated.content, "utf8"),
|
|
4674
|
-
sha256:
|
|
4942
|
+
sha256: rawSha256
|
|
4675
4943
|
},
|
|
4676
4944
|
normalized: {
|
|
4677
4945
|
summary: createSegmentSummary(input.reason, input.strength, signalSummaries),
|
|
@@ -4700,6 +4968,12 @@ async function createSegment(input) {
|
|
|
4700
4968
|
}
|
|
4701
4969
|
};
|
|
4702
4970
|
}
|
|
4971
|
+
function addDays2(value, days) {
|
|
4972
|
+
const timestamp = Date.parse(value);
|
|
4973
|
+
if (!Number.isFinite(timestamp))
|
|
4974
|
+
throw new Error("Invalid Session Evidence retention timestamp.");
|
|
4975
|
+
return new Date(timestamp + days * 24 * 60 * 60 * 1000).toISOString();
|
|
4976
|
+
}
|
|
4703
4977
|
function estimateTokenCount(rawPayloadJson, summary) {
|
|
4704
4978
|
return Math.max(1, Math.ceil((rawPayloadJson.length + summary.length) / 4));
|
|
4705
4979
|
}
|
|
@@ -4914,8 +5188,8 @@ async function updateSessionMemoryFromHook(input) {
|
|
|
4914
5188
|
const cwd = optionalString(input.rawPayload.cwd);
|
|
4915
5189
|
const workspace = team === null && cwd !== null ? await resolveProjectWorkspaceFromCwd({ homeDir: input.homeDir, cwd }) : null;
|
|
4916
5190
|
const projectKey = sanitizeStorageId(team?.projectKey ?? workspace?.projectKey ?? resolveProjectLogKey(input.homeDir, cwd ?? input.homeDir), "project");
|
|
4917
|
-
const
|
|
4918
|
-
const paths = resolveSessionMemoryPaths({ homeDir: input.homeDir, projectKey, sessionKey });
|
|
5191
|
+
const sessionKey2 = sanitizeStorageId(resolveTraceSessionKey(input.rawPayload), "session");
|
|
5192
|
+
const paths = resolveSessionMemoryPaths({ homeDir: input.homeDir, projectKey, sessionKey: sessionKey2 });
|
|
4919
5193
|
const now = normalizeTimestamp(input.receivedAt ?? input.event.time.receivedAt);
|
|
4920
5194
|
const existingState = await readSessionState(paths.statePath);
|
|
4921
5195
|
if (existingState === null && input.event.type !== "UserPromptSubmit") {
|
|
@@ -4927,7 +5201,7 @@ async function updateSessionMemoryFromHook(input) {
|
|
|
4927
5201
|
const currentSignal = createSignal(input, rawEvent, appended.lineNumber);
|
|
4928
5202
|
const state = existingState ?? createInitialState({
|
|
4929
5203
|
projectKey,
|
|
4930
|
-
sessionKey,
|
|
5204
|
+
sessionKey: sessionKey2,
|
|
4931
5205
|
target: input.target,
|
|
4932
5206
|
runId: team?.runId ?? null,
|
|
4933
5207
|
roleId: team?.roleId ?? null,
|
|
@@ -4936,7 +5210,7 @@ async function updateSessionMemoryFromHook(input) {
|
|
|
4936
5210
|
eventId: input.event.eventId,
|
|
4937
5211
|
tokenEstimate
|
|
4938
5212
|
});
|
|
4939
|
-
const cursor = await readSessionCursor(paths.cursorPath,
|
|
5213
|
+
const cursor = await readSessionCursor(paths.cursorPath, sessionKey2, paths.eventsPath, now);
|
|
4940
5214
|
const previous = cloneState(state);
|
|
4941
5215
|
applyEventToState(state, {
|
|
4942
5216
|
input,
|
|
@@ -4978,21 +5252,24 @@ async function updateSessionMemoryFromHook(input) {
|
|
|
4978
5252
|
segmentPath = paths.segmentPath(segment.id);
|
|
4979
5253
|
await writeJson(segmentPath, segment);
|
|
4980
5254
|
await writeSessionIndex(paths, state, segment, now);
|
|
4981
|
-
|
|
4982
|
-
|
|
4983
|
-
|
|
4984
|
-
|
|
4985
|
-
|
|
4986
|
-
|
|
4987
|
-
|
|
4988
|
-
|
|
4989
|
-
|
|
4990
|
-
|
|
4991
|
-
|
|
4992
|
-
|
|
4993
|
-
|
|
4994
|
-
|
|
4995
|
-
|
|
5255
|
+
if (shouldQueueSegmentForEvolution(segment)) {
|
|
5256
|
+
const trigger = await enqueueSegmentEvolutionTrigger({
|
|
5257
|
+
homeDir: input.homeDir,
|
|
5258
|
+
projectKey: segment.projectKey,
|
|
5259
|
+
sessionKey: segment.sessionKey,
|
|
5260
|
+
runId: segment.runId,
|
|
5261
|
+
roleId: segment.roleId,
|
|
5262
|
+
segmentId: segment.id,
|
|
5263
|
+
segmentPath,
|
|
5264
|
+
strength: segment.strength,
|
|
5265
|
+
reason: segment.reason,
|
|
5266
|
+
summary: segment.normalized.summary,
|
|
5267
|
+
now
|
|
5268
|
+
});
|
|
5269
|
+
queuedSegmentTriggerId = trigger.id;
|
|
5270
|
+
}
|
|
5271
|
+
await resetSessionRawEvents(paths.eventsPath);
|
|
5272
|
+
cursor.lastCapturedLine = 0;
|
|
4996
5273
|
cursor.lastCapturedEventId = input.event.eventId;
|
|
4997
5274
|
cursor.updatedAt = now;
|
|
4998
5275
|
state.lastSegmentId = segment.id;
|
|
@@ -5018,6 +5295,9 @@ async function updateSessionMemoryFromHook(input) {
|
|
|
5018
5295
|
queuedSegmentTriggerId
|
|
5019
5296
|
};
|
|
5020
5297
|
}
|
|
5298
|
+
function shouldQueueSegmentForEvolution(segment) {
|
|
5299
|
+
return segment.reason !== "session-memory-threshold" && segment.reason !== "session-memory-init";
|
|
5300
|
+
}
|
|
5021
5301
|
function emptyResult() {
|
|
5022
5302
|
return {
|
|
5023
5303
|
state: null,
|
|
@@ -5029,12 +5309,12 @@ function emptyResult() {
|
|
|
5029
5309
|
};
|
|
5030
5310
|
}
|
|
5031
5311
|
// packages/core/src/hooks/index.ts
|
|
5032
|
-
import { mkdir as mkdir6, readFile as
|
|
5312
|
+
import { mkdir as mkdir6, readFile as readFile8, writeFile as writeFile5 } from "node:fs/promises";
|
|
5033
5313
|
import { dirname as dirname7, join as join11 } from "node:path";
|
|
5034
5314
|
|
|
5035
5315
|
// packages/core/src/team/index.ts
|
|
5036
5316
|
import { spawn } from "node:child_process";
|
|
5037
|
-
import { appendFile as appendFile3, cp, mkdir as mkdir5, readFile as
|
|
5317
|
+
import { appendFile as appendFile3, cp, mkdir as mkdir5, readFile as readFile7, readdir as readdir6, stat as stat3, writeFile as writeFile4 } from "node:fs/promises";
|
|
5038
5318
|
import { basename as basename3, dirname as dirname6, extname, isAbsolute as isAbsolute4, join as join10, relative as relative3, resolve as resolve3 } from "node:path";
|
|
5039
5319
|
|
|
5040
5320
|
// packages/core/src/team/prompts.ts
|
|
@@ -5245,7 +5525,7 @@ function createTeamRunStore(homeDir) {
|
|
|
5245
5525
|
},
|
|
5246
5526
|
async readRun(runId) {
|
|
5247
5527
|
await migrateLegacyRunDirIfNeeded(paths2, runId);
|
|
5248
|
-
return parseTeamRunRecord(JSON.parse(await
|
|
5528
|
+
return parseTeamRunRecord(JSON.parse(await readFile7(paths2.runPath(runId), "utf8")));
|
|
5249
5529
|
},
|
|
5250
5530
|
async writeLatestRunId(runId) {
|
|
5251
5531
|
await mkdir5(paths2.runsDir, { recursive: true });
|
|
@@ -5253,13 +5533,13 @@ function createTeamRunStore(homeDir) {
|
|
|
5253
5533
|
},
|
|
5254
5534
|
async readLatestRunId() {
|
|
5255
5535
|
try {
|
|
5256
|
-
const latest = JSON.parse(await
|
|
5536
|
+
const latest = JSON.parse(await readFile7(paths2.latestRunPath, "utf8"));
|
|
5257
5537
|
if (latest?.version === 1 && typeof latest.runId === "string")
|
|
5258
5538
|
return latest.runId;
|
|
5259
5539
|
return null;
|
|
5260
5540
|
} catch {
|
|
5261
5541
|
try {
|
|
5262
|
-
const latest = JSON.parse(await
|
|
5542
|
+
const latest = JSON.parse(await readFile7(paths2.legacyLatestRunPath, "utf8"));
|
|
5263
5543
|
if (latest?.version === 1 && typeof latest.runId === "string") {
|
|
5264
5544
|
await migrateLegacyRunDirIfNeeded(paths2, latest.runId);
|
|
5265
5545
|
await this.writeLatestRunId(latest.runId);
|
|
@@ -5277,13 +5557,13 @@ function createTeamRunStore(homeDir) {
|
|
|
5277
5557
|
},
|
|
5278
5558
|
async readAgent(runId, roleId) {
|
|
5279
5559
|
await migrateLegacyRunDirIfNeeded(paths2, runId);
|
|
5280
|
-
return parseTeamAgentRecord(JSON.parse(await
|
|
5560
|
+
return parseTeamAgentRecord(JSON.parse(await readFile7(paths2.agentPath(runId, roleId), "utf8")));
|
|
5281
5561
|
},
|
|
5282
5562
|
async readAgents(runId) {
|
|
5283
5563
|
await migrateLegacyRunDirIfNeeded(paths2, runId);
|
|
5284
5564
|
try {
|
|
5285
5565
|
const entries = await readdir6(paths2.agentsDir(runId));
|
|
5286
|
-
const agents = await Promise.all(entries.filter((entry) => entry.endsWith(".json")).map((entry) =>
|
|
5566
|
+
const agents = await Promise.all(entries.filter((entry) => entry.endsWith(".json")).map((entry) => readFile7(join10(paths2.agentsDir(runId), entry), "utf8").then((raw) => parseTeamAgentRecord(JSON.parse(raw)))));
|
|
5287
5567
|
return agents.sort((left, right) => left.roleId.localeCompare(right.roleId));
|
|
5288
5568
|
} catch {
|
|
5289
5569
|
return [];
|
|
@@ -5300,7 +5580,7 @@ function createTeamRunStore(homeDir) {
|
|
|
5300
5580
|
async readMessageList(runId) {
|
|
5301
5581
|
await migrateLegacyRunDirIfNeeded(paths2, runId);
|
|
5302
5582
|
try {
|
|
5303
|
-
const file = parseTeamMessageListFile(JSON.parse(await
|
|
5583
|
+
const file = parseTeamMessageListFile(JSON.parse(await readFile7(paths2.messageListPath(runId), "utf8")));
|
|
5304
5584
|
return file.messages;
|
|
5305
5585
|
} catch (error) {
|
|
5306
5586
|
if (isNotFoundError2(error))
|
|
@@ -6239,7 +6519,7 @@ async function resolveTeamOverlay(input) {
|
|
|
6239
6519
|
return {
|
|
6240
6520
|
source: "repo",
|
|
6241
6521
|
teamPath: repoTeamPath,
|
|
6242
|
-
definition: parseTeamDefinitionMarkdown(await
|
|
6522
|
+
definition: parseTeamDefinitionMarkdown(await readFile7(repoTeamPath, "utf8"))
|
|
6243
6523
|
};
|
|
6244
6524
|
}
|
|
6245
6525
|
const globalTeamPath = resolveGlobalTeamMarkdownPath(input.homeDir);
|
|
@@ -6247,7 +6527,7 @@ async function resolveTeamOverlay(input) {
|
|
|
6247
6527
|
return {
|
|
6248
6528
|
source: "global",
|
|
6249
6529
|
teamPath: globalTeamPath,
|
|
6250
|
-
definition: parseTeamDefinitionMarkdown(await
|
|
6530
|
+
definition: parseTeamDefinitionMarkdown(await readFile7(globalTeamPath, "utf8"))
|
|
6251
6531
|
};
|
|
6252
6532
|
}
|
|
6253
6533
|
return {
|
|
@@ -6262,7 +6542,7 @@ async function ensureDefaultTeamOverlay(input) {
|
|
|
6262
6542
|
const files = [];
|
|
6263
6543
|
for (const asset of assets) {
|
|
6264
6544
|
const targetPath = asset.kind === "team" ? join10(paths2.rootDir, "team", "team.md") : join10(paths2.rootDir, "team", "agents", asset.name);
|
|
6265
|
-
const content = await
|
|
6545
|
+
const content = await readFile7(asset.sourcePath, "utf8");
|
|
6266
6546
|
files.push({
|
|
6267
6547
|
sourcePath: asset.sourcePath,
|
|
6268
6548
|
targetPath,
|
|
@@ -6809,13 +7089,13 @@ async function createScopedTeamStartupContext(input) {
|
|
|
6809
7089
|
});
|
|
6810
7090
|
if (pack === null)
|
|
6811
7091
|
return null;
|
|
6812
|
-
const
|
|
6813
|
-
if (await hasContextInjectionReceipt({ homeDir, sessionKey, contextPackId: pack.id })) {
|
|
7092
|
+
const sessionKey2 = `team-${input.runId}-${input.role.roleId}`;
|
|
7093
|
+
if (await hasContextInjectionReceipt({ homeDir, sessionKey: sessionKey2, contextPackId: pack.id })) {
|
|
6814
7094
|
return null;
|
|
6815
7095
|
}
|
|
6816
7096
|
await writeContextInjectionReceipt({
|
|
6817
7097
|
homeDir,
|
|
6818
|
-
sessionKey,
|
|
7098
|
+
sessionKey: sessionKey2,
|
|
6819
7099
|
pack,
|
|
6820
7100
|
trigger: "team-startup",
|
|
6821
7101
|
hookEventId: null,
|
|
@@ -6934,7 +7214,7 @@ function recoveredHandle(input, window, paneId) {
|
|
|
6934
7214
|
async function readSettingsOrDefault(homeDir) {
|
|
6935
7215
|
const settingsPath = resolveEvoDevPaths(homeDir).settingsPath;
|
|
6936
7216
|
try {
|
|
6937
|
-
return parseSettings(JSON.parse(await
|
|
7217
|
+
return parseSettings(JSON.parse(await readFile7(settingsPath, "utf8")));
|
|
6938
7218
|
} catch {
|
|
6939
7219
|
return createDefaultSettings();
|
|
6940
7220
|
}
|
|
@@ -7012,7 +7292,7 @@ async function assertReadableFile(path, label) {
|
|
|
7012
7292
|
}
|
|
7013
7293
|
async function writeTextFileIfMissing(path, content) {
|
|
7014
7294
|
try {
|
|
7015
|
-
await
|
|
7295
|
+
await readFile7(path, "utf8");
|
|
7016
7296
|
return false;
|
|
7017
7297
|
} catch (error) {
|
|
7018
7298
|
if (!isNotFoundError2(error)) {
|
|
@@ -7045,7 +7325,7 @@ async function readTeamAgentMarkdown(reference) {
|
|
|
7045
7325
|
throw new Error(`Team agent file for ${reference.roleId} must be Markdown.`);
|
|
7046
7326
|
}
|
|
7047
7327
|
try {
|
|
7048
|
-
return await
|
|
7328
|
+
return await readFile7(reference.sourcePath, "utf8");
|
|
7049
7329
|
} catch (error) {
|
|
7050
7330
|
if (isNotFoundError2(error)) {
|
|
7051
7331
|
throw new Error(`Team agent file not found for ${reference.roleId}: ${reference.sourcePath}`);
|
|
@@ -7381,7 +7661,7 @@ function defaultRolePrompt(roleId) {
|
|
|
7381
7661
|
}
|
|
7382
7662
|
async function readRoleCandidate(path, source) {
|
|
7383
7663
|
try {
|
|
7384
|
-
const raw = await
|
|
7664
|
+
const raw = await readFile7(path, "utf8");
|
|
7385
7665
|
return { value: JSON.parse(raw), path, source };
|
|
7386
7666
|
} catch (error) {
|
|
7387
7667
|
if (isNotFoundError2(error))
|
|
@@ -7417,7 +7697,7 @@ function resolveTeamBindingWritePath(input) {
|
|
|
7417
7697
|
}
|
|
7418
7698
|
async function readTeamBindingConfig(path) {
|
|
7419
7699
|
try {
|
|
7420
|
-
const raw = await
|
|
7700
|
+
const raw = await readFile7(path, "utf8");
|
|
7421
7701
|
return parseTeamBindingConfig(JSON.parse(raw));
|
|
7422
7702
|
} catch (error) {
|
|
7423
7703
|
if (isNotFoundError2(error)) {
|
|
@@ -7494,7 +7774,7 @@ async function appendJsonLine2(path, value) {
|
|
|
7494
7774
|
}
|
|
7495
7775
|
async function readJsonLines(path, parse2) {
|
|
7496
7776
|
try {
|
|
7497
|
-
const text2 = await
|
|
7777
|
+
const text2 = await readFile7(path, "utf8");
|
|
7498
7778
|
return text2.split(`
|
|
7499
7779
|
`).filter((line) => line.trim() !== "").map((line) => parse2(JSON.parse(line)));
|
|
7500
7780
|
} catch (error) {
|
|
@@ -7821,8 +8101,20 @@ async function handleHookRuntime(input) {
|
|
|
7821
8101
|
const evolutionDiagnostics = sessionMemoryDiagnostics.queuedSegmentTriggerId !== null || shouldSuppressLegacyEvolutionTrigger(input.event.type) ? { stateWrites: [], warnings: [] } : await recordEvolutionTriggerFromHook(input);
|
|
7822
8102
|
const messageDiagnostics = await deliverPendingTeamMessagesFromHook(input, result);
|
|
7823
8103
|
const scopedContextDiagnostics = await injectScopedKnowledgeContextFromHook(input, result);
|
|
8104
|
+
const knowledgeOutcomeDiagnostics = await recordKnowledgeOutcomeFromHook(input);
|
|
7824
8105
|
const teamStateDiagnostics = await recordTeamAgentHookStateFromHook(input);
|
|
7825
|
-
|
|
8106
|
+
let completed = appendRuntimeDiagnostics(result, diagnostics);
|
|
8107
|
+
for (const entry of [
|
|
8108
|
+
projectDiagnostics,
|
|
8109
|
+
sessionMemoryDiagnostics,
|
|
8110
|
+
evolutionDiagnostics,
|
|
8111
|
+
messageDiagnostics,
|
|
8112
|
+
scopedContextDiagnostics,
|
|
8113
|
+
knowledgeOutcomeDiagnostics,
|
|
8114
|
+
teamStateDiagnostics
|
|
8115
|
+
]) {
|
|
8116
|
+
completed = appendRuntimeDiagnostics(completed, entry);
|
|
8117
|
+
}
|
|
7826
8118
|
return appendDevelopmentHookDiagnostics(input, completed);
|
|
7827
8119
|
}
|
|
7828
8120
|
function formatHookRuntimeOutput(result) {
|
|
@@ -8009,7 +8301,9 @@ async function injectScopedKnowledgeContextFromHook(input, result) {
|
|
|
8009
8301
|
environment: input.environment,
|
|
8010
8302
|
payload: input.rawPayload
|
|
8011
8303
|
});
|
|
8012
|
-
if (team === null
|
|
8304
|
+
if (team === null)
|
|
8305
|
+
return injectOrdinarySessionKnowledge(input, result);
|
|
8306
|
+
if (team.roleId !== "main")
|
|
8013
8307
|
return { stateWrites: [], warnings: [] };
|
|
8014
8308
|
try {
|
|
8015
8309
|
if (input.runtimeInjectionEnabled === false)
|
|
@@ -8021,17 +8315,17 @@ async function injectScopedKnowledgeContextFromHook(input, result) {
|
|
|
8021
8315
|
});
|
|
8022
8316
|
if (pack === null)
|
|
8023
8317
|
return { stateWrites: [], warnings: [] };
|
|
8024
|
-
const
|
|
8318
|
+
const sessionKey2 = resolveHookSessionKey(input.rawPayload);
|
|
8025
8319
|
if (await hasContextInjectionReceipt({
|
|
8026
8320
|
homeDir: input.homeDir,
|
|
8027
|
-
sessionKey,
|
|
8321
|
+
sessionKey: sessionKey2,
|
|
8028
8322
|
contextPackId: pack.id
|
|
8029
8323
|
})) {
|
|
8030
8324
|
return { stateWrites: [], warnings: [] };
|
|
8031
8325
|
}
|
|
8032
8326
|
const receipt = await writeContextInjectionReceipt({
|
|
8033
8327
|
homeDir: input.homeDir,
|
|
8034
|
-
sessionKey,
|
|
8328
|
+
sessionKey: sessionKey2,
|
|
8035
8329
|
pack,
|
|
8036
8330
|
trigger: "hook-safe-point",
|
|
8037
8331
|
hookEventId: input.event.eventId,
|
|
@@ -8046,6 +8340,102 @@ async function injectScopedKnowledgeContextFromHook(input, result) {
|
|
|
8046
8340
|
};
|
|
8047
8341
|
}
|
|
8048
8342
|
}
|
|
8343
|
+
async function injectOrdinarySessionKnowledge(input, result) {
|
|
8344
|
+
if (input.event.type !== "UserPromptSubmit" || input.runtimeInjectionEnabled === false) {
|
|
8345
|
+
return { stateWrites: [], warnings: [] };
|
|
8346
|
+
}
|
|
8347
|
+
const cwd = optionalPayloadString(input.rawPayload.cwd);
|
|
8348
|
+
if (cwd === null)
|
|
8349
|
+
return { stateWrites: [], warnings: [] };
|
|
8350
|
+
const sessionKey2 = resolveHookSessionKey(input.rawPayload);
|
|
8351
|
+
const paths2 = resolveHookRuntimeSessionPaths({ homeDir: input.homeDir, sessionKey: sessionKey2 });
|
|
8352
|
+
const binding = await readSessionBinding(input.homeDir, input.rawPayload);
|
|
8353
|
+
if (binding?.knowledgeContextDeliveredAt !== undefined && binding.knowledgeContextDeliveredAt !== null) {
|
|
8354
|
+
return { stateWrites: [], warnings: [] };
|
|
8355
|
+
}
|
|
8356
|
+
try {
|
|
8357
|
+
const workspace = await resolveProjectWorkspaceFromCwd({ homeDir: input.homeDir, cwd });
|
|
8358
|
+
if (workspace === null)
|
|
8359
|
+
return { stateWrites: [], warnings: [] };
|
|
8360
|
+
const queryText = readKnowledgeQueryText(input.rawPayload);
|
|
8361
|
+
const pack = await createScopedKnowledgeContextPack({
|
|
8362
|
+
homeDir: input.homeDir,
|
|
8363
|
+
projectKey: workspace.projectKey,
|
|
8364
|
+
roleId: input.target,
|
|
8365
|
+
...queryText === null ? {} : { queryText },
|
|
8366
|
+
limit: 3,
|
|
8367
|
+
inlineOnly: true
|
|
8368
|
+
});
|
|
8369
|
+
const deliveredAt = input.receivedAt ?? new Date().toISOString();
|
|
8370
|
+
const nextBinding = {
|
|
8371
|
+
version: 1,
|
|
8372
|
+
target: input.target,
|
|
8373
|
+
sessionKey: sessionKey2,
|
|
8374
|
+
cwd,
|
|
8375
|
+
teamRuntimeContextDeliveredAt: binding?.teamRuntimeContextDeliveredAt ?? null,
|
|
8376
|
+
knowledgeContextDeliveredAt: deliveredAt,
|
|
8377
|
+
updatedAt: deliveredAt
|
|
8378
|
+
};
|
|
8379
|
+
await writeJsonFile2(paths2.bindingPath, nextBinding);
|
|
8380
|
+
if (pack === null)
|
|
8381
|
+
return { stateWrites: [paths2.bindingPath], warnings: [] };
|
|
8382
|
+
const receipt = await writeContextInjectionReceipt({
|
|
8383
|
+
homeDir: input.homeDir,
|
|
8384
|
+
sessionKey: sessionKey2,
|
|
8385
|
+
pack,
|
|
8386
|
+
trigger: "ordinary-session",
|
|
8387
|
+
hookEventId: input.event.eventId,
|
|
8388
|
+
injectedAt: deliveredAt
|
|
8389
|
+
});
|
|
8390
|
+
result.output = appendAdditionalContext(result.output, input.event.type, formatScopedKnowledgePromptBlock(pack));
|
|
8391
|
+
return { stateWrites: [paths2.bindingPath, receipt.path], warnings: [] };
|
|
8392
|
+
} catch {
|
|
8393
|
+
return {
|
|
8394
|
+
stateWrites: [],
|
|
8395
|
+
warnings: ["Ordinary-session knowledge context could not be injected."]
|
|
8396
|
+
};
|
|
8397
|
+
}
|
|
8398
|
+
}
|
|
8399
|
+
function readKnowledgeQueryText(payload) {
|
|
8400
|
+
for (const value of [
|
|
8401
|
+
payload.prompt,
|
|
8402
|
+
payload.user_prompt,
|
|
8403
|
+
payload.userPrompt,
|
|
8404
|
+
payload.query,
|
|
8405
|
+
payload.message,
|
|
8406
|
+
payload.text
|
|
8407
|
+
]) {
|
|
8408
|
+
if (typeof value === "string" && value.trim() !== "")
|
|
8409
|
+
return value.trim().slice(0, 4000);
|
|
8410
|
+
}
|
|
8411
|
+
return null;
|
|
8412
|
+
}
|
|
8413
|
+
async function recordKnowledgeOutcomeFromHook(input) {
|
|
8414
|
+
if (!isKnowledgeOutcomeSignal(input.event))
|
|
8415
|
+
return { stateWrites: [], warnings: [] };
|
|
8416
|
+
try {
|
|
8417
|
+
const paths2 = await recordContextInjectionOutcome({
|
|
8418
|
+
homeDir: input.homeDir,
|
|
8419
|
+
sessionKey: resolveHookSessionKey(input.rawPayload),
|
|
8420
|
+
eventId: input.event.eventId,
|
|
8421
|
+
observedAt: input.receivedAt ?? input.event.time.receivedAt ?? new Date().toISOString(),
|
|
8422
|
+
summary: input.event.payload.summary
|
|
8423
|
+
});
|
|
8424
|
+
return { stateWrites: paths2, warnings: [] };
|
|
8425
|
+
} catch {
|
|
8426
|
+
return {
|
|
8427
|
+
stateWrites: [],
|
|
8428
|
+
warnings: ["Knowledge delivery outcome metadata could not be recorded."]
|
|
8429
|
+
};
|
|
8430
|
+
}
|
|
8431
|
+
}
|
|
8432
|
+
function isKnowledgeOutcomeSignal(event) {
|
|
8433
|
+
if (event.type !== "PostToolUse")
|
|
8434
|
+
return false;
|
|
8435
|
+
if (event.payload.metadata.commandClass === "test-command")
|
|
8436
|
+
return true;
|
|
8437
|
+
return /\b(?:test|lint|typecheck|build|verify|check)\b/iu.test(event.payload.summary);
|
|
8438
|
+
}
|
|
8049
8439
|
function canDeliverTeamMessagesFromHook(target, eventType) {
|
|
8050
8440
|
if (!TEAM_MESSAGE_DELIVERY_EVENTS.has(eventType))
|
|
8051
8441
|
return false;
|
|
@@ -8093,17 +8483,18 @@ async function handleSessionStart(input) {
|
|
|
8093
8483
|
});
|
|
8094
8484
|
}
|
|
8095
8485
|
async function handleUserPromptSubmit(input) {
|
|
8096
|
-
const
|
|
8097
|
-
const paths2 = resolveHookRuntimeSessionPaths({ homeDir: input.homeDir, sessionKey });
|
|
8486
|
+
const sessionKey2 = resolveHookSessionKey(input.rawPayload);
|
|
8487
|
+
const paths2 = resolveHookRuntimeSessionPaths({ homeDir: input.homeDir, sessionKey: sessionKey2 });
|
|
8098
8488
|
const previousBinding = await readSessionBinding(input.homeDir, input.rawPayload);
|
|
8099
8489
|
const teamRuntimeContext = previousBinding?.teamRuntimeContextDeliveredAt === undefined || previousBinding.teamRuntimeContextDeliveredAt === null ? await createTeamRuntimeContextForUserPrompt(input) : null;
|
|
8100
8490
|
const teamRuntimeContextDeliveredAt = teamRuntimeContext !== null ? input.receivedAt ?? new Date().toISOString() : previousBinding?.teamRuntimeContextDeliveredAt ?? null;
|
|
8101
8491
|
const binding = {
|
|
8102
8492
|
version: 1,
|
|
8103
8493
|
target: input.target,
|
|
8104
|
-
sessionKey,
|
|
8494
|
+
sessionKey: sessionKey2,
|
|
8105
8495
|
cwd: optionalPayloadString(input.rawPayload.cwd),
|
|
8106
8496
|
teamRuntimeContextDeliveredAt,
|
|
8497
|
+
knowledgeContextDeliveredAt: previousBinding?.knowledgeContextDeliveredAt ?? null,
|
|
8107
8498
|
updatedAt: input.receivedAt ?? new Date().toISOString()
|
|
8108
8499
|
};
|
|
8109
8500
|
await writeJsonFile2(paths2.bindingPath, binding);
|
|
@@ -8261,10 +8652,10 @@ function truncateTeamMessageBody(value) {
|
|
|
8261
8652
|
return `${value.slice(0, 4000)}...[truncated:${value.length - 4000}]`;
|
|
8262
8653
|
}
|
|
8263
8654
|
async function readSessionBinding(homeDir, payload) {
|
|
8264
|
-
const
|
|
8265
|
-
const paths2 = resolveHookRuntimeSessionPaths({ homeDir, sessionKey });
|
|
8655
|
+
const sessionKey2 = resolveHookSessionKey(payload);
|
|
8656
|
+
const paths2 = resolveHookRuntimeSessionPaths({ homeDir, sessionKey: sessionKey2 });
|
|
8266
8657
|
try {
|
|
8267
|
-
return JSON.parse(await
|
|
8658
|
+
return JSON.parse(await readFile8(paths2.bindingPath, "utf8"));
|
|
8268
8659
|
} catch (error) {
|
|
8269
8660
|
if (isNotFoundError3(error))
|
|
8270
8661
|
return null;
|
|
@@ -8539,7 +8930,7 @@ function mergeSettings(existing, defaults = createDefaultSettings()) {
|
|
|
8539
8930
|
async function readRuntimeInjectionSettings(homeDir) {
|
|
8540
8931
|
const paths2 = resolveEvoDevPaths(homeDir);
|
|
8541
8932
|
try {
|
|
8542
|
-
return parseSettings(JSON.parse(await
|
|
8933
|
+
return parseSettings(JSON.parse(await readFile9(paths2.settingsPath, "utf8"))).memory;
|
|
8543
8934
|
} catch (error) {
|
|
8544
8935
|
if (isNotFoundError4(error))
|
|
8545
8936
|
return createDefaultMemorySettings();
|
|
@@ -8692,7 +9083,7 @@ import { createHash as createHash3, randomUUID as randomUUID2 } from "node:crypt
|
|
|
8692
9083
|
import {
|
|
8693
9084
|
appendFile as appendFile4,
|
|
8694
9085
|
mkdir as mkdir7,
|
|
8695
|
-
readFile as
|
|
9086
|
+
readFile as readFile10,
|
|
8696
9087
|
readdir as readdir7,
|
|
8697
9088
|
rename as rename2,
|
|
8698
9089
|
rm as rm3,
|
|
@@ -8790,7 +9181,7 @@ async function listOkfKnowledgeChangeCandidates(input) {
|
|
|
8790
9181
|
for (const projectKey of projectKeys) {
|
|
8791
9182
|
const paths2 = resolveKnowledgeChangePaths(input.homeDir, projectKey);
|
|
8792
9183
|
for (const name of await listJsonFileNames(paths2.candidatesDir)) {
|
|
8793
|
-
const value = JSON.parse(await
|
|
9184
|
+
const value = JSON.parse(await readFile10(join12(paths2.candidatesDir, name), "utf8"));
|
|
8794
9185
|
const change = parseOkfKnowledgeChangeCandidate(value);
|
|
8795
9186
|
if (input.state === undefined || change.state === input.state)
|
|
8796
9187
|
changes.push(change);
|
|
@@ -8806,7 +9197,7 @@ async function readOkfKnowledgeChangeCandidate(input) {
|
|
|
8806
9197
|
if (input.projectKey !== undefined) {
|
|
8807
9198
|
const paths3 = resolveKnowledgeChangePaths(input.homeDir, input.projectKey);
|
|
8808
9199
|
const path = join12(paths3.candidatesDir, `${changeId}.json`);
|
|
8809
|
-
const change2 = parseOkfKnowledgeChangeCandidate(JSON.parse(await
|
|
9200
|
+
const change2 = parseOkfKnowledgeChangeCandidate(JSON.parse(await readFile10(path, "utf8")));
|
|
8810
9201
|
return { path, change: change2 };
|
|
8811
9202
|
}
|
|
8812
9203
|
const matches = await listOkfKnowledgeChangeCandidates({ homeDir: input.homeDir });
|
|
@@ -8827,7 +9218,7 @@ async function replaceOkfKnowledgeChangeCandidate(input) {
|
|
|
8827
9218
|
}
|
|
8828
9219
|
const paths2 = resolveKnowledgeChangePaths(input.homeDir, input.next.projectKey);
|
|
8829
9220
|
const path = join12(paths2.candidatesDir, `${input.next.id}.json`);
|
|
8830
|
-
const current = parseOkfKnowledgeChangeCandidate(JSON.parse(await
|
|
9221
|
+
const current = parseOkfKnowledgeChangeCandidate(JSON.parse(await readFile10(path, "utf8")));
|
|
8831
9222
|
if (current.state !== input.previous.state || current.updatedAt !== input.previous.updatedAt || current.candidate.revision !== input.previous.candidate.revision) {
|
|
8832
9223
|
throw new Error("Knowledge change was modified by another decision.");
|
|
8833
9224
|
}
|
|
@@ -8936,7 +9327,7 @@ function resolveKnowledgeChangePaths(homeDir, projectKey) {
|
|
|
8936
9327
|
}
|
|
8937
9328
|
async function readKnowledgeChangeLockToken(path) {
|
|
8938
9329
|
try {
|
|
8939
|
-
const value = JSON.parse(await
|
|
9330
|
+
const value = JSON.parse(await readFile10(path, "utf8"));
|
|
8940
9331
|
return isRecord6(value) && typeof value.token === "string" ? value.token : null;
|
|
8941
9332
|
} catch {
|
|
8942
9333
|
return null;
|
|
@@ -8967,7 +9358,7 @@ async function recoverStaleKnowledgeChangeLock(path) {
|
|
|
8967
9358
|
}
|
|
8968
9359
|
async function readKnowledgeChangeLockOwner(path) {
|
|
8969
9360
|
try {
|
|
8970
|
-
const value = JSON.parse(await
|
|
9361
|
+
const value = JSON.parse(await readFile10(path, "utf8"));
|
|
8971
9362
|
if (!isRecord6(value))
|
|
8972
9363
|
return null;
|
|
8973
9364
|
return {
|
|
@@ -9092,6 +9483,7 @@ function createOkfKnowledgeRuntimeProjectionFromCandidate(candidate) {
|
|
|
9092
9483
|
pathScopes: candidate.pathScopes
|
|
9093
9484
|
},
|
|
9094
9485
|
relatedConceptLinks: candidate.relatedConceptLinks,
|
|
9486
|
+
supportRef: candidate.supportRef ?? null,
|
|
9095
9487
|
lifecycle: {
|
|
9096
9488
|
status: candidate.decision === "revoke" ? "revoked" : "active",
|
|
9097
9489
|
supersedes: [],
|
|
@@ -9125,6 +9517,7 @@ function createOkfKnowledgeRuntimeProjectionFromConcept(concept) {
|
|
|
9125
9517
|
pathScopes: concept.pathScopes
|
|
9126
9518
|
},
|
|
9127
9519
|
relatedConceptLinks: readSectionList(sections, "Related Concepts").filter((item) => item !== "No related concepts yet."),
|
|
9520
|
+
supportRef: concept.supportRef ?? null,
|
|
9128
9521
|
lifecycle: {
|
|
9129
9522
|
status: concept.lifecycle.status,
|
|
9130
9523
|
supersedes: concept.lifecycle.supersedes,
|
|
@@ -9290,6 +9683,7 @@ function normalizeRuntimeProjection(value) {
|
|
|
9290
9683
|
pathScopes: normalizeArray(value.scopes.pathScopes)
|
|
9291
9684
|
},
|
|
9292
9685
|
relatedConceptLinks: normalizeArray(value.relatedConceptLinks),
|
|
9686
|
+
supportRef: value.supportRef,
|
|
9293
9687
|
lifecycle: {
|
|
9294
9688
|
status: value.lifecycle.status,
|
|
9295
9689
|
supersedes: normalizeArray(value.lifecycle.supersedes),
|
|
@@ -9317,6 +9711,7 @@ function listChangedProjectionFields(base, candidate) {
|
|
|
9317
9711
|
["scopes.workflowTags", base.scopes.workflowTags, candidate.scopes.workflowTags],
|
|
9318
9712
|
["scopes.pathScopes", base.scopes.pathScopes, candidate.scopes.pathScopes],
|
|
9319
9713
|
["relatedConceptLinks", base.relatedConceptLinks, candidate.relatedConceptLinks],
|
|
9714
|
+
["supportRef", base.supportRef, candidate.supportRef],
|
|
9320
9715
|
["lifecycle", base.lifecycle, candidate.lifecycle]
|
|
9321
9716
|
];
|
|
9322
9717
|
return fields.filter(([, left, right]) => stableJsonStringify(left) !== stableJsonStringify(right)).map(([field]) => field);
|
|
@@ -9383,36 +9778,103 @@ function stableJsonStringify(value) {
|
|
|
9383
9778
|
return `{${entries.map(([key, child]) => `${JSON.stringify(key)}:${stableJsonStringify(child)}`).join(",")}}`;
|
|
9384
9779
|
}
|
|
9385
9780
|
|
|
9386
|
-
// packages/core/src/evolution/knowledge/
|
|
9387
|
-
function
|
|
9781
|
+
// packages/core/src/evolution/knowledge/support.ts
|
|
9782
|
+
function resolveOkfKnowledgeRuntimeEligibility(input) {
|
|
9388
9783
|
const now = normalizeNow(input.now);
|
|
9389
|
-
|
|
9390
|
-
if (input.verifiedContradiction === true || concept.lifecycle.status === "stale" || concept.lifecycle.status === "deprecated" || concept.lifecycle.status === "revoked" || concept.lifecycle.status === "superseded" || concept.reviewState === "stale" || concept.reviewState === "deprecated" || concept.reviewState === "revoked" || concept.reviewState === "superseded" || isDue(concept.lifecycle.staleAfter, now)) {
|
|
9784
|
+
if (input.verifiedContradiction === true || input.lifecycle.status === "stale" || input.lifecycle.status === "deprecated" || input.lifecycle.status === "revoked" || input.lifecycle.status === "superseded" || input.reviewState === "stale" || input.reviewState === "deprecated" || input.reviewState === "revoked" || input.reviewState === "superseded" || isDue(input.lifecycle.staleAfter, now)) {
|
|
9391
9785
|
return {
|
|
9786
|
+
delivery: "blocked",
|
|
9392
9787
|
freshness: "stale",
|
|
9393
|
-
reason: input.verifiedContradiction === true ? "Verified evidence contradicts the active claim." : "Lifecycle
|
|
9788
|
+
reason: input.verifiedContradiction === true ? "Verified evidence contradicts the active claim." : "Lifecycle state excludes this knowledge from runtime context."
|
|
9394
9789
|
};
|
|
9395
9790
|
}
|
|
9396
|
-
if (
|
|
9791
|
+
if (input.reviewState !== "accepted" && input.reviewState !== "auto-accepted") {
|
|
9397
9792
|
return {
|
|
9793
|
+
delivery: "blocked",
|
|
9398
9794
|
freshness: "unknown",
|
|
9399
|
-
reason: "
|
|
9795
|
+
reason: "Knowledge has not passed an active-compatible review state."
|
|
9400
9796
|
};
|
|
9401
9797
|
}
|
|
9402
|
-
if (input.
|
|
9798
|
+
if (input.sourceStatus === "changed") {
|
|
9403
9799
|
return {
|
|
9800
|
+
delivery: "blocked",
|
|
9404
9801
|
freshness: "review-due",
|
|
9405
|
-
reason:
|
|
9802
|
+
reason: "The supporting source changed and requires revalidation."
|
|
9803
|
+
};
|
|
9804
|
+
}
|
|
9805
|
+
if (isDue(input.lifecycle.reviewAfter, now)) {
|
|
9806
|
+
return {
|
|
9807
|
+
delivery: "reference",
|
|
9808
|
+
freshness: "review-due",
|
|
9809
|
+
reason: `Review was due at ${input.lifecycle.reviewAfter}.`
|
|
9810
|
+
};
|
|
9811
|
+
}
|
|
9812
|
+
if (input.supportRef === null) {
|
|
9813
|
+
return {
|
|
9814
|
+
delivery: "reference",
|
|
9815
|
+
freshness: "unknown",
|
|
9816
|
+
reason: "Legacy knowledge has no structured support reference."
|
|
9817
|
+
};
|
|
9818
|
+
}
|
|
9819
|
+
if (input.supportRef.kind === "semantic-inference") {
|
|
9820
|
+
if (input.reviewState === "accepted") {
|
|
9821
|
+
return {
|
|
9822
|
+
delivery: "inline",
|
|
9823
|
+
freshness: "verified",
|
|
9824
|
+
reason: "A human-accepted inference is runtime-eligible as an explicit decision."
|
|
9825
|
+
};
|
|
9826
|
+
}
|
|
9827
|
+
return {
|
|
9828
|
+
delivery: "reference",
|
|
9829
|
+
freshness: "unknown",
|
|
9830
|
+
reason: "Model-inferred knowledge remains on-demand until supported by a direct source."
|
|
9831
|
+
};
|
|
9832
|
+
}
|
|
9833
|
+
if ((input.supportRef.kind === "repo-policy" || input.supportRef.kind === "state-observation") && input.sourceStatus !== "current") {
|
|
9834
|
+
return {
|
|
9835
|
+
delivery: "reference",
|
|
9836
|
+
freshness: "unknown",
|
|
9837
|
+
reason: "The supporting repository source has not been revalidated for this query."
|
|
9406
9838
|
};
|
|
9407
9839
|
}
|
|
9408
9840
|
return {
|
|
9841
|
+
delivery: "inline",
|
|
9409
9842
|
freshness: "verified",
|
|
9410
|
-
reason: `
|
|
9843
|
+
reason: `Runtime-eligible through ${input.supportRef.kind}.`
|
|
9844
|
+
};
|
|
9845
|
+
}
|
|
9846
|
+
function isDirectKnowledgeSupport(supportRef) {
|
|
9847
|
+
return supportRef !== undefined && supportRef !== null && supportRef.kind !== "semantic-inference";
|
|
9848
|
+
}
|
|
9849
|
+
function normalizeNow(value) {
|
|
9850
|
+
if (value instanceof Date && Number.isFinite(value.getTime()))
|
|
9851
|
+
return value;
|
|
9852
|
+
if (typeof value === "string" && Number.isFinite(Date.parse(value)))
|
|
9853
|
+
return new Date(value);
|
|
9854
|
+
return new Date;
|
|
9855
|
+
}
|
|
9856
|
+
function isDue(value, now) {
|
|
9857
|
+
const timestamp = Date.parse(value);
|
|
9858
|
+
return Number.isFinite(timestamp) && timestamp <= now.getTime();
|
|
9859
|
+
}
|
|
9860
|
+
|
|
9861
|
+
// packages/core/src/evolution/knowledge/freshness.ts
|
|
9862
|
+
function deriveOkfKnowledgeFreshness(input) {
|
|
9863
|
+
const eligibility = resolveOkfKnowledgeRuntimeEligibility({
|
|
9864
|
+
reviewState: input.concept.reviewState,
|
|
9865
|
+
lifecycle: input.concept.lifecycle,
|
|
9866
|
+
supportRef: input.concept.supportRef ?? null,
|
|
9867
|
+
now: input.now,
|
|
9868
|
+
sourceStatus: input.repositoryFingerprintChanged === true ? "changed" : input.repositoryFingerprintChanged === false ? "current" : undefined,
|
|
9869
|
+
verifiedContradiction: input.verifiedContradiction
|
|
9870
|
+
});
|
|
9871
|
+
return {
|
|
9872
|
+
freshness: eligibility.freshness,
|
|
9873
|
+
reason: eligibility.reason
|
|
9411
9874
|
};
|
|
9412
9875
|
}
|
|
9413
9876
|
function createOkfKnowledgeVerificationSnapshot(input) {
|
|
9414
|
-
|
|
9415
|
-
if (!hasVerification)
|
|
9877
|
+
if (!isDirectKnowledgeSupport(input.candidate.supportRef))
|
|
9416
9878
|
return null;
|
|
9417
9879
|
const verifiedAt = normalizeIso(input.verifiedAt);
|
|
9418
9880
|
if (verifiedAt === null)
|
|
@@ -9424,20 +9886,6 @@ function createOkfKnowledgeVerificationSnapshot(input) {
|
|
|
9424
9886
|
repository: null
|
|
9425
9887
|
};
|
|
9426
9888
|
}
|
|
9427
|
-
function normalizeNow(value) {
|
|
9428
|
-
if (value instanceof Date && Number.isFinite(value.getTime()))
|
|
9429
|
-
return value;
|
|
9430
|
-
if (typeof value === "string") {
|
|
9431
|
-
const normalized = normalizeIso(value);
|
|
9432
|
-
if (normalized !== null)
|
|
9433
|
-
return new Date(normalized);
|
|
9434
|
-
}
|
|
9435
|
-
return new Date;
|
|
9436
|
-
}
|
|
9437
|
-
function isDue(value, now) {
|
|
9438
|
-
const normalized = normalizeIso(value);
|
|
9439
|
-
return normalized !== null && Date.parse(normalized) <= now.getTime();
|
|
9440
|
-
}
|
|
9441
9889
|
function normalizeIso(value) {
|
|
9442
9890
|
const time2 = Date.parse(value);
|
|
9443
9891
|
return Number.isFinite(time2) ? new Date(time2).toISOString() : null;
|
|
@@ -9813,6 +10261,7 @@ function normalizePlanCandidate(value, path) {
|
|
|
9813
10261
|
scores: normalizeScores(input.scores, `${path}.scores`),
|
|
9814
10262
|
decisionReason: sanitizeOkfText(readRequiredString(input.decisionReason, `${path}.decisionReason`)),
|
|
9815
10263
|
evidenceRefs: readStringArray(input.evidenceRefs, `${path}.evidenceRefs`).map(sanitizeOkfText),
|
|
10264
|
+
supportRef: input.supportRef === undefined ? undefined : normalizeKnowledgeSupportRef(input.supportRef, `${path}.supportRef`),
|
|
9816
10265
|
reviewState: parseOkfReviewState(readRequiredString(input.reviewState, `${path}.reviewState`)),
|
|
9817
10266
|
verificationNotApplicableReason: typeof input.verificationNotApplicableReason === "string" ? sanitizeOkfText(input.verificationNotApplicableReason) : undefined,
|
|
9818
10267
|
evalSetRefs: input.evalSetRefs === undefined ? undefined : readStringArray(input.evalSetRefs, `${path}.evalSetRefs`).map(sanitizeOkfText),
|
|
@@ -9872,6 +10321,7 @@ function validateCandidateContract(candidate, path, evalSetIds, add, options) {
|
|
|
9872
10321
|
validateStringArrayValue(candidate.pathScopes, `${path}.pathScopes`, add);
|
|
9873
10322
|
validateStringArrayValue(candidate.relatedConceptLinks, `${path}.relatedConceptLinks`, add);
|
|
9874
10323
|
validateStringArrayValue(candidate.evidenceRefs, `${path}.evidenceRefs`, add);
|
|
10324
|
+
validateKnowledgeSupportRefValue(candidate.supportRef, `${path}.supportRef`, add);
|
|
9875
10325
|
validateOverlayUpdatesValue(candidate.overlayUpdates, `${path}.overlayUpdates`, add);
|
|
9876
10326
|
validateScoresValue(candidate.scores, `${path}.scores`, add);
|
|
9877
10327
|
validatePrivacyCheckValue(candidate.privacyCheck, `${path}.privacyCheck`, add);
|
|
@@ -10086,6 +10536,24 @@ function normalizeBasis(value) {
|
|
|
10086
10536
|
throw new Error("Candidate basis is invalid.");
|
|
10087
10537
|
return value;
|
|
10088
10538
|
}
|
|
10539
|
+
function normalizeKnowledgeSupportRef(value, path) {
|
|
10540
|
+
const input = assertRecordValue(value, path);
|
|
10541
|
+
const kind = readRequiredString(input.kind, `${path}.kind`);
|
|
10542
|
+
if (kind !== "user-declaration" && kind !== "repo-policy" && kind !== "state-observation" && kind !== "verified-outcome" && kind !== "semantic-inference") {
|
|
10543
|
+
throw new Error(`${path}.kind is invalid.`);
|
|
10544
|
+
}
|
|
10545
|
+
const observedAt = readRequiredString(input.observedAt, `${path}.observedAt`);
|
|
10546
|
+
if (!Number.isFinite(Date.parse(observedAt)))
|
|
10547
|
+
throw new Error(`${path}.observedAt is invalid.`);
|
|
10548
|
+
const subjectFingerprint = input.subjectFingerprint === null ? null : sanitizeOkfText(readRequiredString(input.subjectFingerprint, `${path}.subjectFingerprint`));
|
|
10549
|
+
return {
|
|
10550
|
+
id: sanitizeOkfText(readRequiredString(input.id, `${path}.id`)),
|
|
10551
|
+
kind,
|
|
10552
|
+
sourceRefId: sanitizeOkfText(readRequiredString(input.sourceRefId, `${path}.sourceRefId`)),
|
|
10553
|
+
subjectFingerprint,
|
|
10554
|
+
observedAt: new Date(observedAt).toISOString()
|
|
10555
|
+
};
|
|
10556
|
+
}
|
|
10089
10557
|
function normalizeEvalSetDecision(value) {
|
|
10090
10558
|
const normalized = value === "no-write" ? "no_write" : value;
|
|
10091
10559
|
if (normalized !== "create" && normalized !== "needs-human" && normalized !== "no_write" && normalized !== "skip") {
|
|
@@ -10208,6 +10676,28 @@ function validateStringArrayValue(value, path, add) {
|
|
|
10208
10676
|
add(`${path}[${index}]`, "array.string", "Array items must be non-empty strings.");
|
|
10209
10677
|
});
|
|
10210
10678
|
}
|
|
10679
|
+
function validateKnowledgeSupportRefValue(value, path, add) {
|
|
10680
|
+
if (value === undefined)
|
|
10681
|
+
return;
|
|
10682
|
+
if (!isRecord7(value)) {
|
|
10683
|
+
add(path, "supportRef.object", "Candidate supportRef must be an object.");
|
|
10684
|
+
return;
|
|
10685
|
+
}
|
|
10686
|
+
if (!isNonEmptyString2(value.id))
|
|
10687
|
+
add(`${path}.id`, "supportRef.id", "supportRef id is required.");
|
|
10688
|
+
if (value.kind !== "user-declaration" && value.kind !== "repo-policy" && value.kind !== "state-observation" && value.kind !== "verified-outcome" && value.kind !== "semantic-inference") {
|
|
10689
|
+
add(`${path}.kind`, "supportRef.kind", "supportRef kind is invalid.");
|
|
10690
|
+
}
|
|
10691
|
+
if (!isNonEmptyString2(value.sourceRefId)) {
|
|
10692
|
+
add(`${path}.sourceRefId`, "supportRef.sourceRefId", "supportRef sourceRefId is required.");
|
|
10693
|
+
}
|
|
10694
|
+
if (value.subjectFingerprint !== null && !isNonEmptyString2(value.subjectFingerprint)) {
|
|
10695
|
+
add(`${path}.subjectFingerprint`, "supportRef.subjectFingerprint", "supportRef subjectFingerprint must be null or a non-empty string.");
|
|
10696
|
+
}
|
|
10697
|
+
if (!isNonEmptyString2(value.observedAt) || !Number.isFinite(Date.parse(value.observedAt))) {
|
|
10698
|
+
add(`${path}.observedAt`, "supportRef.observedAt", "supportRef observedAt must be an ISO timestamp.");
|
|
10699
|
+
}
|
|
10700
|
+
}
|
|
10211
10701
|
function validateOverlayUpdatesValue(value, path, add) {
|
|
10212
10702
|
if (!Array.isArray(value)) {
|
|
10213
10703
|
add(path, "overlayUpdates.array", "Overlay updates must be an array.");
|
|
@@ -10411,7 +10901,7 @@ function decideOkfKnowledgeCandidate(candidate, context) {
|
|
|
10411
10901
|
const localActiveTarget = sanitized.targetStore === "okf" || sanitized.kind === "evos-case";
|
|
10412
10902
|
const hasScopeTags = sanitized.repoTags.length > 0 && sanitized.roleTags.length > 0;
|
|
10413
10903
|
const highRiskRequiresHuman = hasHighRiskHumanReviewSignal(candidate) || hasHighRiskHumanReviewSignal(sanitized);
|
|
10414
|
-
const autoAcceptEligible = !highRiskRequiresHuman && !isLegacyTemplateKnowledge(sanitized) && !hasEphemeralKnowledgeIdentity(sanitized) && hasReusableSemanticShape(sanitized) && conflict === null && localActiveTarget && sanitized.targetStore === "okf" && sanitized.basis === "direct" && sanitized.metadataOnlyEvidence && hasVerification && hasScopeTags && scores.evidenceStrength >= 4 && scores.reuseValue >= 3 && scores.actionability >= 3 && scores.stability >= 3 && scores.privacyRisk <= 2 && scores.duplicationRisk <= 2;
|
|
10904
|
+
const autoAcceptEligible = !highRiskRequiresHuman && !isLegacyTemplateKnowledge(sanitized) && !hasEphemeralKnowledgeIdentity(sanitized) && hasReusableSemanticShape(sanitized) && conflict === null && localActiveTarget && sanitized.targetStore === "okf" && sanitized.basis === "direct" && isDirectKnowledgeSupport(sanitized.supportRef) && sanitized.metadataOnlyEvidence && hasVerification && hasScopeTags && scores.evidenceStrength >= 4 && scores.reuseValue >= 3 && scores.actionability >= 3 && scores.stability >= 3 && scores.privacyRisk <= 2 && scores.duplicationRisk <= 2;
|
|
10415
10905
|
if (autoAcceptEligible) {
|
|
10416
10906
|
return {
|
|
10417
10907
|
...sanitized,
|
|
@@ -10738,7 +11228,7 @@ async function preflightOkfConceptTargets(okfDir, candidates) {
|
|
|
10738
11228
|
targets.set(targetPath, { action: "write", candidateId: candidate.id });
|
|
10739
11229
|
continue;
|
|
10740
11230
|
}
|
|
10741
|
-
const existing = parseOkfConceptFile(okfDir, targetPath, await
|
|
11231
|
+
const existing = parseOkfConceptFile(okfDir, targetPath, await readFile11(targetPath, "utf8"));
|
|
10742
11232
|
if (existing === null) {
|
|
10743
11233
|
throw new Error(`OKF concept target is occupied by an unrecognized file: ${candidate.targetPath}`);
|
|
10744
11234
|
}
|
|
@@ -10789,7 +11279,7 @@ async function writeFailedOkfKnowledgePlanArtifact(input) {
|
|
|
10789
11279
|
}
|
|
10790
11280
|
async function readFailedOkfKnowledgePlanArtifact(input) {
|
|
10791
11281
|
const path = resolveFailedPlanPath(input.homeDir, input.projectKey, input.runId);
|
|
10792
|
-
const value = JSON.parse(await
|
|
11282
|
+
const value = JSON.parse(await readFile11(path, "utf8"));
|
|
10793
11283
|
if (isRecord7(value) && value.kind === "okf-knowledge-failed-plan") {
|
|
10794
11284
|
const artifact = value;
|
|
10795
11285
|
validateFailedPlanArtifact(artifact);
|
|
@@ -10975,7 +11465,7 @@ async function listOkfKnowledgeConcepts(input) {
|
|
|
10975
11465
|
for (const file of files) {
|
|
10976
11466
|
if (RESERVED_OKF_FILENAMES.has(file.name))
|
|
10977
11467
|
continue;
|
|
10978
|
-
const content = await
|
|
11468
|
+
const content = await readFile11(file.path, "utf8");
|
|
10979
11469
|
const parsed = parseOkfConceptFile(okfDir, file.path, content);
|
|
10980
11470
|
if (parsed !== null)
|
|
10981
11471
|
concepts.push(parsed);
|
|
@@ -11014,7 +11504,7 @@ async function queryOkfKnowledge(input) {
|
|
|
11014
11504
|
for (const file of files) {
|
|
11015
11505
|
if (RESERVED_OKF_FILENAMES.has(file.name))
|
|
11016
11506
|
continue;
|
|
11017
|
-
const content = await
|
|
11507
|
+
const content = await readFile11(file.path, "utf8");
|
|
11018
11508
|
const sourceLink = toOkfLink(okfDir, file.path);
|
|
11019
11509
|
if (isUnsafeOkfQueryContent(content)) {
|
|
11020
11510
|
warnings.push(`Omitted unsafe OKF knowledge item: ${sourceLink}. Run evodev knowledge lint.`);
|
|
@@ -11291,8 +11781,8 @@ function mergeAcceptedEvosCasesIntoKnowledgeQuery(result, cases, warnings = [],
|
|
|
11291
11781
|
score: structuredScore + lexical.score,
|
|
11292
11782
|
title: evosCase.title,
|
|
11293
11783
|
summary: evosCase.expectedFutureBehavior || evosCase.result.summary || evosCase.title,
|
|
11294
|
-
freshness: "
|
|
11295
|
-
runtimeExcerpt:
|
|
11784
|
+
freshness: "unknown",
|
|
11785
|
+
runtimeExcerpt: null,
|
|
11296
11786
|
matchReasons: lexical.reasons.length === 0 ? structuredReasons : [...structuredReasons, ...lexical.reasons]
|
|
11297
11787
|
}
|
|
11298
11788
|
];
|
|
@@ -11305,8 +11795,12 @@ function mergeAcceptedEvosCasesIntoKnowledgeQuery(result, cases, warnings = [],
|
|
|
11305
11795
|
};
|
|
11306
11796
|
}
|
|
11307
11797
|
async function createScopedKnowledgeContextPack(input) {
|
|
11308
|
-
const result = await queryScopedOkfKnowledgeContext(
|
|
11309
|
-
|
|
11798
|
+
const result = await queryScopedOkfKnowledgeContext({
|
|
11799
|
+
...input,
|
|
11800
|
+
limit: input.inlineOnly === true ? Math.max(input.limit ?? 3, 50) : input.limit
|
|
11801
|
+
});
|
|
11802
|
+
const selectedItems = input.inlineOnly === true ? result.items.filter((item) => item.runtimeExcerpt !== null).slice(0, input.limit ?? 3) : result.items;
|
|
11803
|
+
if (selectedItems.length === 0)
|
|
11310
11804
|
return null;
|
|
11311
11805
|
const scope = {
|
|
11312
11806
|
...result.projectKey === undefined ? {} : { projectKey: result.projectKey },
|
|
@@ -11314,7 +11808,7 @@ async function createScopedKnowledgeContextPack(input) {
|
|
|
11314
11808
|
...result.workflowId === undefined ? {} : { workflowId: result.workflowId },
|
|
11315
11809
|
paths: result.paths
|
|
11316
11810
|
};
|
|
11317
|
-
const items =
|
|
11811
|
+
const items = selectedItems.map((item) => ({
|
|
11318
11812
|
id: item.id,
|
|
11319
11813
|
sourceType: item.sourceType,
|
|
11320
11814
|
sourceLink: item.sourceLink,
|
|
@@ -11331,10 +11825,11 @@ async function createScopedKnowledgeContextPack(input) {
|
|
|
11331
11825
|
}));
|
|
11332
11826
|
const okfIndexRevision = createKnowledgeContextRevision(items);
|
|
11333
11827
|
const queryText = normalizeKnowledgeQueryText(result.queryText);
|
|
11828
|
+
const queryHash = queryText === undefined ? undefined : sha256Short2(queryText);
|
|
11334
11829
|
const packSeed = stableJsonStringify2({
|
|
11335
11830
|
okfIndexRevision,
|
|
11336
11831
|
scope,
|
|
11337
|
-
|
|
11832
|
+
queryHash: queryHash ?? null,
|
|
11338
11833
|
items: items.map((item) => ({
|
|
11339
11834
|
id: item.id,
|
|
11340
11835
|
sourceType: item.sourceType,
|
|
@@ -11352,7 +11847,7 @@ async function createScopedKnowledgeContextPack(input) {
|
|
|
11352
11847
|
id: `ctxpack-${sha256Short2(packSeed)}`,
|
|
11353
11848
|
okfIndexRevision,
|
|
11354
11849
|
scope,
|
|
11355
|
-
...
|
|
11850
|
+
...queryHash === undefined ? {} : { queryHash },
|
|
11356
11851
|
items,
|
|
11357
11852
|
warnings: result.warnings.length === 0 ? [] : [
|
|
11358
11853
|
"Some scoped knowledge items were omitted because they failed safety validation; run evodev knowledge lint."
|
|
@@ -11367,7 +11862,6 @@ function formatScopedKnowledgePromptBlock(pack) {
|
|
|
11367
11862
|
`Project: ${pack.scope.projectKey ?? "all"}`,
|
|
11368
11863
|
`Role: ${pack.scope.roleId ?? "any"}`,
|
|
11369
11864
|
`Workflow: ${pack.scope.workflowId ?? "any"}`,
|
|
11370
|
-
...pack.queryText === undefined ? [] : [`Query: ${sanitizeOkfText(pack.queryText)}`],
|
|
11371
11865
|
`Paths: ${pack.scope.paths.length === 0 ? "all" : pack.scope.paths.join(", ")}`,
|
|
11372
11866
|
"Raw content stored: false",
|
|
11373
11867
|
"Freshness is determined by EvoDev lifecycle and verification metadata; do not infer validity from timestamps.",
|
|
@@ -11405,9 +11899,11 @@ async function writeContextInjectionReceipt(input) {
|
|
|
11405
11899
|
okfIndexRevision: input.pack.okfIndexRevision,
|
|
11406
11900
|
scope: input.pack.scope,
|
|
11407
11901
|
itemIds: input.pack.items.map((item) => item.id),
|
|
11902
|
+
queryHash: input.pack.queryHash ?? null,
|
|
11408
11903
|
injectedAt: input.injectedAt ?? new Date().toISOString(),
|
|
11409
11904
|
hookEventId: input.hookEventId ?? null,
|
|
11410
11905
|
trigger: input.trigger,
|
|
11906
|
+
outcome: null,
|
|
11411
11907
|
rawContentStored: false
|
|
11412
11908
|
};
|
|
11413
11909
|
const path = resolveContextInjectionReceiptPath({
|
|
@@ -11418,6 +11914,42 @@ async function writeContextInjectionReceipt(input) {
|
|
|
11418
11914
|
await writeJson3(path, receipt, { overwrite: true });
|
|
11419
11915
|
return { path, receipt };
|
|
11420
11916
|
}
|
|
11917
|
+
async function recordContextInjectionOutcome(input) {
|
|
11918
|
+
const stateDir = resolveEvoDevPaths(input.homeDir).stateDir;
|
|
11919
|
+
const directory = join13(stateDir, "context-injections", sanitizeReceiptPathSegment(input.sessionKey));
|
|
11920
|
+
let names;
|
|
11921
|
+
try {
|
|
11922
|
+
names = await readdir8(directory);
|
|
11923
|
+
} catch {
|
|
11924
|
+
return [];
|
|
11925
|
+
}
|
|
11926
|
+
const paths2 = [];
|
|
11927
|
+
for (const name of names.filter((item) => item.endsWith(".json")).sort()) {
|
|
11928
|
+
const path = join13(directory, name);
|
|
11929
|
+
let receipt;
|
|
11930
|
+
try {
|
|
11931
|
+
receipt = JSON.parse(await readFile11(path, "utf8"));
|
|
11932
|
+
} catch {
|
|
11933
|
+
continue;
|
|
11934
|
+
}
|
|
11935
|
+
if (receipt.version !== 1 || receipt.rawContentStored !== false || receipt.outcome !== null && receipt.outcome !== undefined) {
|
|
11936
|
+
continue;
|
|
11937
|
+
}
|
|
11938
|
+
const updated = {
|
|
11939
|
+
...receipt,
|
|
11940
|
+
outcome: {
|
|
11941
|
+
status: "verified",
|
|
11942
|
+
eventId: sanitizeReceiptPathSegment(input.eventId),
|
|
11943
|
+
observedAt: normalizeTimestamp(input.observedAt),
|
|
11944
|
+
summaryHash: sha256Short2(input.summary),
|
|
11945
|
+
rawContentStored: false
|
|
11946
|
+
}
|
|
11947
|
+
};
|
|
11948
|
+
await writeJson3(path, updated, { overwrite: true });
|
|
11949
|
+
paths2.push(path);
|
|
11950
|
+
}
|
|
11951
|
+
return paths2;
|
|
11952
|
+
}
|
|
11421
11953
|
async function markOkfKnowledgeConceptStale(input) {
|
|
11422
11954
|
const target = await readMutableOkfConcept(input.homeDir, input.conceptId);
|
|
11423
11955
|
const lifecycle = {
|
|
@@ -11514,7 +12046,7 @@ async function readMutableOkfConcept(homeDir, conceptId) {
|
|
|
11514
12046
|
for (const file of files) {
|
|
11515
12047
|
if (RESERVED_OKF_FILENAMES.has(file.name))
|
|
11516
12048
|
continue;
|
|
11517
|
-
const content = await
|
|
12049
|
+
const content = await readFile11(file.path, "utf8");
|
|
11518
12050
|
const concept = parseOkfConceptFile(paths2.okfDir, file.path, content);
|
|
11519
12051
|
if (concept === null)
|
|
11520
12052
|
continue;
|
|
@@ -11626,7 +12158,7 @@ async function lintOkfKnowledge(input) {
|
|
|
11626
12158
|
const knownConceptIds = new Set(files.filter((file) => !RESERVED_OKF_FILENAMES.has(file.name)).map((file) => toOkfRelativePath(paths2.okfDir, file.path).replace(/\.md$/, "")));
|
|
11627
12159
|
for (const file of files) {
|
|
11628
12160
|
const rel = displayOkfPath(paths2.okfDir, file.path);
|
|
11629
|
-
const content = await
|
|
12161
|
+
const content = await readFile11(file.path, "utf8");
|
|
11630
12162
|
if (file.name === "index.md") {
|
|
11631
12163
|
if (file.path !== join13(paths2.okfDir, "index.md") && hasFrontmatter(content)) {
|
|
11632
12164
|
errors2.push(`Non-root index.md must not have frontmatter: ${rel}`);
|
|
@@ -11972,6 +12504,9 @@ function explainAutomaticNoWriteDecision(candidate, scores, hasVerification, has
|
|
|
11972
12504
|
reasons.push("target is not user-local OKF");
|
|
11973
12505
|
if (candidate.basis !== "direct")
|
|
11974
12506
|
reasons.push("basis is inferred");
|
|
12507
|
+
if (!isDirectKnowledgeSupport(candidate.supportRef)) {
|
|
12508
|
+
reasons.push("structured direct support is missing");
|
|
12509
|
+
}
|
|
11975
12510
|
if (!candidate.metadataOnlyEvidence)
|
|
11976
12511
|
reasons.push("evidence is not metadata-only");
|
|
11977
12512
|
if (!hasVerification)
|
|
@@ -12115,7 +12650,7 @@ async function ensureOverlayConcept(input) {
|
|
|
12115
12650
|
const exists = await pathExists3(input.overlayPath);
|
|
12116
12651
|
const linkLine = `- [${input.candidate.title}](${input.link}) - ${input.candidate.description}`;
|
|
12117
12652
|
if (exists) {
|
|
12118
|
-
const current = await
|
|
12653
|
+
const current = await readFile11(input.overlayPath, "utf8");
|
|
12119
12654
|
if (current.includes(input.link))
|
|
12120
12655
|
return;
|
|
12121
12656
|
await writeFile7(input.overlayPath, `${current.trimEnd()}
|
|
@@ -12222,6 +12757,7 @@ function renderOkfConcept(candidate, plan, existing) {
|
|
|
12222
12757
|
` stableKey: ${yamlString(candidate.stableKey)}`,
|
|
12223
12758
|
` reviewState: ${yamlString(reviewState)}`,
|
|
12224
12759
|
renderLifecycleYaml(" ", lifecycle),
|
|
12760
|
+
renderKnowledgeSupportRefYaml(" ", candidate.supportRef ?? null),
|
|
12225
12761
|
renderVerificationSnapshotYaml(" ", verificationSnapshot),
|
|
12226
12762
|
" source:",
|
|
12227
12763
|
` kind: ${yamlString("trace-distillation")}`,
|
|
@@ -12339,7 +12875,7 @@ async function regenerateOkfDirectoryIndexes(okfDir) {
|
|
|
12339
12875
|
continue;
|
|
12340
12876
|
}
|
|
12341
12877
|
const filePath = join13(directory, entry.name);
|
|
12342
|
-
const concept = parseOkfConceptFile(okfDir, filePath, await
|
|
12878
|
+
const concept = parseOkfConceptFile(okfDir, filePath, await readFile11(filePath, "utf8"));
|
|
12343
12879
|
if (concept !== null && !isActiveOkfConcept(concept))
|
|
12344
12880
|
continue;
|
|
12345
12881
|
const title = concept?.title ?? titleFromSlug(entry.name.replace(/\.md$/, ""));
|
|
@@ -12395,7 +12931,7 @@ async function appendOkfLog(okfDir, entry) {
|
|
|
12395
12931
|
async function prependLogEntry(logPath, entry) {
|
|
12396
12932
|
await mkdir8(dirname9(logPath), { recursive: true });
|
|
12397
12933
|
const date = todayIsoDate();
|
|
12398
|
-
const existing = await pathExists3(logPath) ? await
|
|
12934
|
+
const existing = await pathExists3(logPath) ? await readFile11(logPath, "utf8") : `# Directory Update Log
|
|
12399
12935
|
`;
|
|
12400
12936
|
const line = `* ${entry}`;
|
|
12401
12937
|
if (existing.includes(`## ${date}`)) {
|
|
@@ -12445,6 +12981,7 @@ function parseOkfConceptFile(okfDir, filePath, content) {
|
|
|
12445
12981
|
lifecycle: lifecycleParsed.lifecycle,
|
|
12446
12982
|
lifecyclePersisted: lifecycleParsed.persisted,
|
|
12447
12983
|
verificationSnapshot: parseOkfVerificationSnapshot(frontmatter),
|
|
12984
|
+
supportRef: parseOkfKnowledgeSupportRef(frontmatter),
|
|
12448
12985
|
title,
|
|
12449
12986
|
description,
|
|
12450
12987
|
tags,
|
|
@@ -12455,6 +12992,22 @@ function parseOkfConceptFile(okfDir, filePath, content) {
|
|
|
12455
12992
|
body: parsed.body
|
|
12456
12993
|
};
|
|
12457
12994
|
}
|
|
12995
|
+
function parseOkfKnowledgeSupportRef(frontmatter) {
|
|
12996
|
+
const block = extractNestedYamlBlock(frontmatter, "evodev", "supportRef");
|
|
12997
|
+
if (block === null)
|
|
12998
|
+
return null;
|
|
12999
|
+
try {
|
|
13000
|
+
return normalizeKnowledgeSupportRef({
|
|
13001
|
+
id: readIndentedYamlScalar(block, "id"),
|
|
13002
|
+
kind: readIndentedYamlScalar(block, "kind"),
|
|
13003
|
+
sourceRefId: readIndentedYamlScalar(block, "sourceRefId"),
|
|
13004
|
+
subjectFingerprint: readNullableLifecycleString(readIndentedYamlScalar(block, "subjectFingerprint")),
|
|
13005
|
+
observedAt: readIndentedYamlScalar(block, "observedAt")
|
|
13006
|
+
}, "evodev.supportRef");
|
|
13007
|
+
} catch {
|
|
13008
|
+
return null;
|
|
13009
|
+
}
|
|
13010
|
+
}
|
|
12458
13011
|
function parseOkfVerificationSnapshot(frontmatter) {
|
|
12459
13012
|
const block = extractNestedYamlBlock(frontmatter, "evodev", "verificationSnapshot");
|
|
12460
13013
|
if (block === null)
|
|
@@ -13028,6 +13581,19 @@ function renderVerificationSnapshotYaml(indent, snapshot) {
|
|
|
13028
13581
|
].join(`
|
|
13029
13582
|
`);
|
|
13030
13583
|
}
|
|
13584
|
+
function renderKnowledgeSupportRefYaml(indent, supportRef) {
|
|
13585
|
+
if (supportRef === null)
|
|
13586
|
+
return `${indent}supportRef: null`;
|
|
13587
|
+
return [
|
|
13588
|
+
`${indent}supportRef:`,
|
|
13589
|
+
`${indent} id: ${yamlString(supportRef.id)}`,
|
|
13590
|
+
`${indent} kind: ${yamlString(supportRef.kind)}`,
|
|
13591
|
+
`${indent} sourceRefId: ${yamlString(supportRef.sourceRefId)}`,
|
|
13592
|
+
`${indent} subjectFingerprint: ${yamlNullableString(supportRef.subjectFingerprint)}`,
|
|
13593
|
+
`${indent} observedAt: ${yamlString(supportRef.observedAt)}`
|
|
13594
|
+
].join(`
|
|
13595
|
+
`);
|
|
13596
|
+
}
|
|
13031
13597
|
function lifecycleStatusFromReviewState(reviewState) {
|
|
13032
13598
|
if (reviewState === "stale")
|
|
13033
13599
|
return "stale";
|
|
@@ -13193,7 +13759,7 @@ async function createLegacyGeneratedKnowledgeCleanupPlan(homeDir, generatedAt) {
|
|
|
13193
13759
|
for (const file of await listMarkdownFiles(conceptRoot)) {
|
|
13194
13760
|
if (RESERVED_OKF_FILENAMES.has(file.name))
|
|
13195
13761
|
continue;
|
|
13196
|
-
const content = await
|
|
13762
|
+
const content = await readFile11(file.path, "utf8");
|
|
13197
13763
|
if (!isLegacyGeneratedOkfConcept(file.name, content))
|
|
13198
13764
|
continue;
|
|
13199
13765
|
concepts.push(createLegacyCleanupArtifact({
|
|
@@ -13206,7 +13772,7 @@ async function createLegacyGeneratedKnowledgeCleanupPlan(homeDir, generatedAt) {
|
|
|
13206
13772
|
}
|
|
13207
13773
|
const evosCases = [];
|
|
13208
13774
|
for (const file of await listJsonFiles2(evoDevPaths.evosCasesDir)) {
|
|
13209
|
-
const content = await
|
|
13775
|
+
const content = await readFile11(file.path, "utf8");
|
|
13210
13776
|
if (!isLegacyGeneratedEvosCase(content))
|
|
13211
13777
|
continue;
|
|
13212
13778
|
evosCases.push(createLegacyCleanupArtifact({
|
|
@@ -13227,7 +13793,7 @@ async function createLegacyGeneratedKnowledgeCleanupPlan(homeDir, generatedAt) {
|
|
|
13227
13793
|
for (const file of await listMarkdownFiles(join13(okfPaths.okfDir, root))) {
|
|
13228
13794
|
if (RESERVED_OKF_FILENAMES.has(file.name))
|
|
13229
13795
|
continue;
|
|
13230
|
-
const content = await
|
|
13796
|
+
const content = await readFile11(file.path, "utf8");
|
|
13231
13797
|
const cleanedContent = removeLegacyOverlayLinks(content, conceptLinks);
|
|
13232
13798
|
if (cleanedContent === content)
|
|
13233
13799
|
continue;
|
|
@@ -13551,7 +14117,7 @@ function parseAgentProfile(value) {
|
|
|
13551
14117
|
return { ...profile, permissions };
|
|
13552
14118
|
}
|
|
13553
14119
|
async function readAgentProfile(path) {
|
|
13554
|
-
return parseAgentProfile(JSON.parse(await
|
|
14120
|
+
return parseAgentProfile(JSON.parse(await readFile12(path, "utf8")));
|
|
13555
14121
|
}
|
|
13556
14122
|
function loadAgentContextDryRun(profile) {
|
|
13557
14123
|
const parsed = parseAgentProfile(profile);
|
|
@@ -13776,7 +14342,7 @@ function expectRelativePath(value, path) {
|
|
|
13776
14342
|
return text2;
|
|
13777
14343
|
}
|
|
13778
14344
|
// packages/core/src/assets/scanner.ts
|
|
13779
|
-
import { readFile as
|
|
14345
|
+
import { readFile as readFile13, readdir as readdir9, stat as stat6 } from "node:fs/promises";
|
|
13780
14346
|
import { join as join14, relative as relative5 } from "node:path";
|
|
13781
14347
|
async function scanAssets(paths2) {
|
|
13782
14348
|
const [skills, agents] = await Promise.all([
|
|
@@ -13833,7 +14399,7 @@ async function readScannedAsset(rootDir, manifestPath, parseManifest) {
|
|
|
13833
14399
|
async function readManifestJson(manifestPath) {
|
|
13834
14400
|
let raw;
|
|
13835
14401
|
try {
|
|
13836
|
-
raw = await
|
|
14402
|
+
raw = await readFile13(manifestPath, "utf8");
|
|
13837
14403
|
} catch (error) {
|
|
13838
14404
|
throw new EvoDevAssetError(`Cannot read manifest (${describeFileError(error)})`, manifestPath);
|
|
13839
14405
|
}
|
|
@@ -13902,7 +14468,7 @@ __export(exports_code_agent_traces, {
|
|
|
13902
14468
|
resolveCodeAgentTraceRefPaths: () => resolveCodeAgentTraceRefPaths,
|
|
13903
14469
|
writeCodeAgentTraceRef: () => writeCodeAgentTraceRef
|
|
13904
14470
|
});
|
|
13905
|
-
import { mkdir as mkdir9, readFile as
|
|
14471
|
+
import { mkdir as mkdir9, readFile as readFile14, readdir as readdir10, stat as stat7, writeFile as writeFile8 } from "node:fs/promises";
|
|
13906
14472
|
import { dirname as dirname10, isAbsolute as isAbsolute6, join as join15, normalize, relative as relative6 } from "node:path";
|
|
13907
14473
|
var CODE_AGENT_TRACE_TARGETS = ["claude", "codex"];
|
|
13908
14474
|
var CODE_AGENT_TRACE_REF_SOURCES = [
|
|
@@ -13922,7 +14488,7 @@ function resolveCodeAgentTraceRefPaths(homeDir) {
|
|
|
13922
14488
|
};
|
|
13923
14489
|
}
|
|
13924
14490
|
function createCodeAgentTraceRef(input) {
|
|
13925
|
-
const
|
|
14491
|
+
const sessionKey2 = sanitizePersistentIdentifier2(input.sessionKey, "session");
|
|
13926
14492
|
const nativeSessionId = input.nativeSessionId ?? null;
|
|
13927
14493
|
const nativeSessionIdHash = input.nativeSessionIdHash === undefined ? hashOptionalIdentifier2(nativeSessionId) : sanitizeHashMetadata(input.nativeSessionIdHash);
|
|
13928
14494
|
const cwdHash = input.cwdHash === undefined ? hashOptionalIdentifier2(input.cwd ?? null) : sanitizeHashMetadata(input.cwdHash);
|
|
@@ -13933,9 +14499,9 @@ function createCodeAgentTraceRef(input) {
|
|
|
13933
14499
|
});
|
|
13934
14500
|
return {
|
|
13935
14501
|
version: 1,
|
|
13936
|
-
id: `trace-ref-${input.target}-${
|
|
14502
|
+
id: `trace-ref-${input.target}-${sessionKey2}`,
|
|
13937
14503
|
target: input.target,
|
|
13938
|
-
sessionKey,
|
|
14504
|
+
sessionKey: sessionKey2,
|
|
13939
14505
|
nativeSessionIdHash,
|
|
13940
14506
|
projectKey: input.projectKey === undefined || input.projectKey === null ? null : sanitizePersistentIdentifier2(input.projectKey, "project"),
|
|
13941
14507
|
runId: input.runId === undefined || input.runId === null ? null : sanitizePersistentIdentifier2(input.runId, "run"),
|
|
@@ -14003,7 +14569,7 @@ async function readCodeAgentTraceRef(input) {
|
|
|
14003
14569
|
target: targetAndSession.target,
|
|
14004
14570
|
sessionKey: targetAndSession.sessionKey
|
|
14005
14571
|
});
|
|
14006
|
-
const ref = sanitizeCodeAgentTraceRefForWrite(parseCodeAgentTraceRef(JSON.parse(await
|
|
14572
|
+
const ref = sanitizeCodeAgentTraceRefForWrite(parseCodeAgentTraceRef(JSON.parse(await readFile14(path, "utf8"))));
|
|
14007
14573
|
if (ref.id !== input.id)
|
|
14008
14574
|
throw new Error(`Code Agent trace ref id mismatch: ${input.id}`);
|
|
14009
14575
|
return { ref, path };
|
|
@@ -14018,7 +14584,7 @@ async function listCodeAgentTraceRefs(input) {
|
|
|
14018
14584
|
if (!entry.isFile() || !entry.name.endsWith(".json"))
|
|
14019
14585
|
continue;
|
|
14020
14586
|
const path = join15(dir, entry.name);
|
|
14021
|
-
const ref = sanitizeCodeAgentTraceRefForWrite(parseCodeAgentTraceRef(JSON.parse(await
|
|
14587
|
+
const ref = sanitizeCodeAgentTraceRefForWrite(parseCodeAgentTraceRef(JSON.parse(await readFile14(path, "utf8"))));
|
|
14022
14588
|
if (input.projectKey !== undefined && ref.projectKey !== input.projectKey)
|
|
14023
14589
|
continue;
|
|
14024
14590
|
if (input.runId !== undefined && ref.runId !== input.runId)
|
|
@@ -14379,7 +14945,7 @@ function expectNonNegativeInteger(value, path) {
|
|
|
14379
14945
|
return value;
|
|
14380
14946
|
}
|
|
14381
14947
|
// packages/core/src/config/store.ts
|
|
14382
|
-
import { mkdir as mkdir10, readFile as
|
|
14948
|
+
import { mkdir as mkdir10, readFile as readFile15, writeFile as writeFile9 } from "node:fs/promises";
|
|
14383
14949
|
import { dirname as dirname11 } from "node:path";
|
|
14384
14950
|
function createCoreConfigStore(homeDir) {
|
|
14385
14951
|
const paths2 = resolveEvoDevPaths(homeDir);
|
|
@@ -14513,7 +15079,7 @@ async function ensureKnowledgeBaseFiles(paths2) {
|
|
|
14513
15079
|
async function readJsonFile2(filePath, parse2) {
|
|
14514
15080
|
let raw;
|
|
14515
15081
|
try {
|
|
14516
|
-
raw = await
|
|
15082
|
+
raw = await readFile15(filePath, "utf8");
|
|
14517
15083
|
} catch (error) {
|
|
14518
15084
|
throw new EvoDevConfigError(`Cannot read config file (${describeFileError2(error)})`, filePath);
|
|
14519
15085
|
}
|
|
@@ -14544,7 +15110,7 @@ async function readJsonFileOrDefault(filePath, parse2, fallback) {
|
|
|
14544
15110
|
}
|
|
14545
15111
|
async function writeIfMissing(filePath, value) {
|
|
14546
15112
|
try {
|
|
14547
|
-
await
|
|
15113
|
+
await readFile15(filePath, "utf8");
|
|
14548
15114
|
} catch (error) {
|
|
14549
15115
|
if (isNodeError2(error) && error.code === "ENOENT") {
|
|
14550
15116
|
await writeJsonFile3(filePath, value);
|
|
@@ -14556,7 +15122,7 @@ async function writeIfMissing(filePath, value) {
|
|
|
14556
15122
|
async function writeIndexIfMissingOrMigrate(filePath, kind, defaults) {
|
|
14557
15123
|
let raw;
|
|
14558
15124
|
try {
|
|
14559
|
-
raw = await
|
|
15125
|
+
raw = await readFile15(filePath, "utf8");
|
|
14560
15126
|
} catch (error) {
|
|
14561
15127
|
if (isNodeError2(error) && error.code === "ENOENT") {
|
|
14562
15128
|
await writeJsonFile3(filePath, defaults);
|
|
@@ -14579,7 +15145,7 @@ async function writeIndexIfMissingOrMigrate(filePath, kind, defaults) {
|
|
|
14579
15145
|
}
|
|
14580
15146
|
async function writeTextIfMissing2(filePath, value) {
|
|
14581
15147
|
try {
|
|
14582
|
-
await
|
|
15148
|
+
await readFile15(filePath, "utf8");
|
|
14583
15149
|
} catch (error) {
|
|
14584
15150
|
if (isNodeError2(error) && error.code === "ENOENT") {
|
|
14585
15151
|
await mkdir10(dirname11(filePath), { recursive: true });
|
|
@@ -14608,7 +15174,7 @@ function isRecord10(value) {
|
|
|
14608
15174
|
}
|
|
14609
15175
|
// packages/core/src/daemon/index.ts
|
|
14610
15176
|
import { randomBytes } from "node:crypto";
|
|
14611
|
-
import { mkdir as mkdir13, readFile as
|
|
15177
|
+
import { mkdir as mkdir13, readFile as readFile19, readdir as readdir13, rm as rm6, stat as stat10, writeFile as writeFile11 } from "node:fs/promises";
|
|
14612
15178
|
import { createServer } from "node:http";
|
|
14613
15179
|
import { dirname as dirname14, join as join20 } from "node:path";
|
|
14614
15180
|
|
|
@@ -14761,7 +15327,7 @@ __export(exports_processor, {
|
|
|
14761
15327
|
});
|
|
14762
15328
|
|
|
14763
15329
|
// packages/core/src/evolution/evidence/analysis.ts
|
|
14764
|
-
import { readFile as
|
|
15330
|
+
import { readFile as readFile16, readdir as readdir11 } from "node:fs/promises";
|
|
14765
15331
|
import { basename as basename4, join as join16 } from "node:path";
|
|
14766
15332
|
async function analyzeEvolutionRun(input) {
|
|
14767
15333
|
const projectKey = resolveEvolutionProjectKey(input);
|
|
@@ -14877,7 +15443,7 @@ async function analyzeEvolutionRun(input) {
|
|
|
14877
15443
|
};
|
|
14878
15444
|
}
|
|
14879
15445
|
async function readExecutionEvidenceEvents(input) {
|
|
14880
|
-
const raw = await
|
|
15446
|
+
const raw = await readFile16(input.path, "utf8");
|
|
14881
15447
|
const lines = raw.split(`
|
|
14882
15448
|
`);
|
|
14883
15449
|
const events = [];
|
|
@@ -14947,7 +15513,7 @@ function createEvidenceEventFromExecutionEvent(input) {
|
|
|
14947
15513
|
};
|
|
14948
15514
|
}
|
|
14949
15515
|
async function readTraceEvidenceEvents(input) {
|
|
14950
|
-
const raw = await
|
|
15516
|
+
const raw = await readFile16(input.path, "utf8");
|
|
14951
15517
|
const lines = raw.split(`
|
|
14952
15518
|
`);
|
|
14953
15519
|
const events = [];
|
|
@@ -15514,7 +16080,7 @@ function createProvenance(evidenceWindow, createdAt, sourceRefs) {
|
|
|
15514
16080
|
}
|
|
15515
16081
|
// packages/core/src/evolution/processor/process.ts
|
|
15516
16082
|
import { randomUUID as randomUUID3 } from "node:crypto";
|
|
15517
|
-
import { mkdir as mkdir11, open as open2, readFile as
|
|
16083
|
+
import { mkdir as mkdir11, open as open2, readFile as readFile17, rm as rm5, stat as stat8 } from "node:fs/promises";
|
|
15518
16084
|
import { dirname as dirname12, join as join18 } from "node:path";
|
|
15519
16085
|
var PROCESS_LOCK_STALE_MS2 = 5 * 60 * 1000;
|
|
15520
16086
|
var PROCESS_LOCK_HEARTBEAT_MS = 30 * 1000;
|
|
@@ -15940,7 +16506,7 @@ async function assertEvolutionProcessLockOwned(lock) {
|
|
|
15940
16506
|
}
|
|
15941
16507
|
async function evolutionProcessLockIsOwned(lock) {
|
|
15942
16508
|
try {
|
|
15943
|
-
const value = JSON.parse(await
|
|
16509
|
+
const value = JSON.parse(await readFile17(lock.path, "utf8"));
|
|
15944
16510
|
return value.ownerId === lock.ownerId;
|
|
15945
16511
|
} catch {
|
|
15946
16512
|
return false;
|
|
@@ -16003,7 +16569,7 @@ function isTransientEvolutionError(error) {
|
|
|
16003
16569
|
return /(?:timed?\s*out|timeout|rate[\s-]*limit|too many requests|\b429\b|network|econn(?:reset|refused|aborted)|eai_again|etimedout|socket|temporar|service unavailable|\b50[0234]\b|fetch failed)/u.test(message);
|
|
16004
16570
|
}
|
|
16005
16571
|
// packages/core/src/observability/index.ts
|
|
16006
|
-
import { mkdir as mkdir12, readFile as
|
|
16572
|
+
import { mkdir as mkdir12, readFile as readFile18, readdir as readdir12, stat as stat9, writeFile as writeFile10 } from "node:fs/promises";
|
|
16007
16573
|
import { dirname as dirname13, join as join19 } from "node:path";
|
|
16008
16574
|
var EVENT_TYPE_TO_DIR = {
|
|
16009
16575
|
"verification.completed": "verification",
|
|
@@ -16084,7 +16650,7 @@ async function listObservabilityEvents(homeDir, type) {
|
|
|
16084
16650
|
const path = resolveObservabilityStorePaths(homeDir, eventType).eventsPath;
|
|
16085
16651
|
if (!await pathExists5(path))
|
|
16086
16652
|
continue;
|
|
16087
|
-
const lines = (await
|
|
16653
|
+
const lines = (await readFile18(path, "utf8")).split(`
|
|
16088
16654
|
`).filter(Boolean);
|
|
16089
16655
|
for (const line of lines) {
|
|
16090
16656
|
const event = JSON.parse(line);
|
|
@@ -16261,7 +16827,7 @@ async function readDaemonLock(homeDir) {
|
|
|
16261
16827
|
const paths2 = resolveDaemonPaths(homeDir);
|
|
16262
16828
|
if (!await pathExists6(paths2.lockPath))
|
|
16263
16829
|
return null;
|
|
16264
|
-
const lock = JSON.parse(await
|
|
16830
|
+
const lock = JSON.parse(await readFile19(paths2.lockPath, "utf8"));
|
|
16265
16831
|
if (lock.version !== 1 || lock.component !== "evodev-daemon")
|
|
16266
16832
|
throw new Error("Invalid daemon lock.");
|
|
16267
16833
|
return lock;
|
|
@@ -16270,7 +16836,7 @@ async function readDaemonToken(homeDir) {
|
|
|
16270
16836
|
const paths2 = resolveDaemonPaths(homeDir);
|
|
16271
16837
|
if (!await pathExists6(paths2.tokenPath))
|
|
16272
16838
|
return null;
|
|
16273
|
-
return (await
|
|
16839
|
+
return (await readFile19(paths2.tokenPath, "utf8")).trim();
|
|
16274
16840
|
}
|
|
16275
16841
|
async function cleanupDaemonState(homeDir, token) {
|
|
16276
16842
|
const paths2 = resolveDaemonPaths(homeDir);
|
|
@@ -16476,7 +17042,7 @@ async function collectLearningCandidateSummaries(homeDir, warnings) {
|
|
|
16476
17042
|
warnings.push("Learning candidate store not found; returning empty candidates.");
|
|
16477
17043
|
return [];
|
|
16478
17044
|
}
|
|
16479
|
-
const lines = (await
|
|
17045
|
+
const lines = (await readFile19(path, "utf8")).split(`
|
|
16480
17046
|
`).filter(Boolean);
|
|
16481
17047
|
return lines.map((line) => {
|
|
16482
17048
|
const candidate = JSON.parse(line);
|
|
@@ -16783,7 +17349,7 @@ async function readDaemonEvolutionProcessError(homeDir) {
|
|
|
16783
17349
|
const path = resolveDaemonEvolutionProcessErrorPath(homeDir);
|
|
16784
17350
|
if (!await pathExists6(path))
|
|
16785
17351
|
return null;
|
|
16786
|
-
const value = JSON.parse(await
|
|
17352
|
+
const value = JSON.parse(await readFile19(path, "utf8"));
|
|
16787
17353
|
return typeof value.summary === "string" && value.summary.length > 0 ? value.summary : null;
|
|
16788
17354
|
}
|
|
16789
17355
|
function describeError3(error) {
|
|
@@ -17231,7 +17797,7 @@ function assertDescendant(rootDir, candidate) {
|
|
|
17231
17797
|
}
|
|
17232
17798
|
// packages/core/src/evolution/imports/storage.ts
|
|
17233
17799
|
import { randomUUID as randomUUID4 } from "node:crypto";
|
|
17234
|
-
import { chmod as chmod2, link as link2, lstat as lstat3, mkdir as mkdir14, open as open3, readFile as
|
|
17800
|
+
import { chmod as chmod2, link as link2, lstat as lstat3, mkdir as mkdir14, open as open3, readFile as readFile20, rename as rename4, rm as rm7 } from "node:fs/promises";
|
|
17235
17801
|
import { dirname as dirname15, join as join22 } from "node:path";
|
|
17236
17802
|
var PRIVATE_DIRECTORY_MODE = 448;
|
|
17237
17803
|
var PRIVATE_FILE_MODE = 384;
|
|
@@ -17411,7 +17977,7 @@ async function writeTrajectoryImportManifest(paths2, manifest) {
|
|
|
17411
17977
|
}
|
|
17412
17978
|
async function readTrajectoryImportIndex(path) {
|
|
17413
17979
|
await assertRegularStateFile(path);
|
|
17414
|
-
const content = await
|
|
17980
|
+
const content = await readFile20(path, "utf8");
|
|
17415
17981
|
const lines = content.endsWith(`
|
|
17416
17982
|
`) ? content.slice(0, -1).split(`
|
|
17417
17983
|
`) : content.split(`
|
|
@@ -17524,7 +18090,7 @@ async function heartbeatTrajectoryImportSourceLock(lock) {
|
|
|
17524
18090
|
}
|
|
17525
18091
|
async function trajectoryImportSourceLockIsOwned(lock) {
|
|
17526
18092
|
try {
|
|
17527
|
-
const value = JSON.parse(await
|
|
18093
|
+
const value = JSON.parse(await readFile20(lock.ownerPath, "utf8"));
|
|
17528
18094
|
return value.ownerToken === lock.ownerToken;
|
|
17529
18095
|
} catch {
|
|
17530
18096
|
return false;
|
|
@@ -17608,7 +18174,7 @@ async function restoreQuarantinedLock(quarantinePath, targetPath) {
|
|
|
17608
18174
|
}
|
|
17609
18175
|
async function removeOwnedControlFile(path, ownerToken) {
|
|
17610
18176
|
try {
|
|
17611
|
-
const value = JSON.parse(await
|
|
18177
|
+
const value = JSON.parse(await readFile20(path, "utf8"));
|
|
17612
18178
|
if (value.ownerToken === ownerToken)
|
|
17613
18179
|
await rm7(path, { force: true });
|
|
17614
18180
|
} catch {}
|
|
@@ -17635,7 +18201,7 @@ async function writePrivateImmutableFile(path, content) {
|
|
|
17635
18201
|
if (!hasErrorCode(error, "EEXIST"))
|
|
17636
18202
|
throw error;
|
|
17637
18203
|
await assertRegularStateFile(path);
|
|
17638
|
-
const existing = await
|
|
18204
|
+
const existing = await readFile20(path, "utf8");
|
|
17639
18205
|
if (existing !== content) {
|
|
17640
18206
|
throw new Error("Trajectory import immutable state digest conflict.");
|
|
17641
18207
|
}
|
|
@@ -17690,7 +18256,7 @@ async function createPrivateExclusiveFile(path, content) {
|
|
|
17690
18256
|
async function readOptionalJson(path, parser) {
|
|
17691
18257
|
try {
|
|
17692
18258
|
await assertRegularStateFile(path);
|
|
17693
|
-
const content = await
|
|
18259
|
+
const content = await readFile20(path, "utf8");
|
|
17694
18260
|
return parser(JSON.parse(content));
|
|
17695
18261
|
} catch (error) {
|
|
17696
18262
|
if (hasErrorCode(error, "ENOENT"))
|
|
@@ -18302,8 +18868,8 @@ function createHistoricalSessionEvidenceSegments(input) {
|
|
|
18302
18868
|
throw new Error("Historical materialization requires a projectKey.");
|
|
18303
18869
|
}
|
|
18304
18870
|
const createdAt = normalizeIsoTimestamp(input.createdAt);
|
|
18305
|
-
const expiresAt =
|
|
18306
|
-
const
|
|
18871
|
+
const expiresAt = addDays3(createdAt, input.plan.policy.retentionDays);
|
|
18872
|
+
const sessionKey2 = `historical-${input.plan.preview.sourceKey}`;
|
|
18307
18873
|
const target = sourceTarget(input.plan.preview.source);
|
|
18308
18874
|
return input.plan.preparedSegments.map((prepared) => ({
|
|
18309
18875
|
schemaVersion: 1,
|
|
@@ -18312,7 +18878,7 @@ function createHistoricalSessionEvidenceSegments(input) {
|
|
|
18312
18878
|
projectKey,
|
|
18313
18879
|
runId: null,
|
|
18314
18880
|
roleId: input.plan.preview.scope.roleId,
|
|
18315
|
-
sessionKey,
|
|
18881
|
+
sessionKey: sessionKey2,
|
|
18316
18882
|
target,
|
|
18317
18883
|
createdAt,
|
|
18318
18884
|
reason: "historical-import",
|
|
@@ -18710,7 +19276,7 @@ function normalizeIsoTimestamp(value) {
|
|
|
18710
19276
|
}
|
|
18711
19277
|
return new Date(value).toISOString();
|
|
18712
19278
|
}
|
|
18713
|
-
function
|
|
19279
|
+
function addDays3(value, days) {
|
|
18714
19280
|
return new Date(Date.parse(value) + days * 24 * 60 * 60 * 1000).toISOString();
|
|
18715
19281
|
}
|
|
18716
19282
|
// packages/core/src/evolution/imports/apply.ts
|
|
@@ -18757,13 +19323,13 @@ async function applyTrajectoryImport(input) {
|
|
|
18757
19323
|
plan: input.plan,
|
|
18758
19324
|
createdAt: staged.receipt.createdAt
|
|
18759
19325
|
});
|
|
18760
|
-
const
|
|
19326
|
+
const sessionKey2 = `historical-${input.plan.preview.sourceKey}`;
|
|
18761
19327
|
const sessionPaths = resolveSessionMemoryPaths({
|
|
18762
19328
|
homeDir: input.homeDir,
|
|
18763
19329
|
projectKey,
|
|
18764
|
-
sessionKey
|
|
19330
|
+
sessionKey: sessionKey2
|
|
18765
19331
|
});
|
|
18766
|
-
const expectedTriggerIds = segments.map((segment) => createStableId("segment-trigger", [projectKey,
|
|
19332
|
+
const expectedTriggerIds = segments.map((segment) => createStableId("segment-trigger", [projectKey, sessionKey2, segment.id]));
|
|
18767
19333
|
let progress = await resolveMaterializationProgress({
|
|
18768
19334
|
path: paths2.materializationPath,
|
|
18769
19335
|
previewId: input.plan.preview.id,
|
|
@@ -18796,7 +19362,7 @@ async function applyTrajectoryImport(input) {
|
|
|
18796
19362
|
homeDir: input.homeDir,
|
|
18797
19363
|
paths: sessionPaths,
|
|
18798
19364
|
projectKey,
|
|
18799
|
-
sessionKey,
|
|
19365
|
+
sessionKey: sessionKey2,
|
|
18800
19366
|
target: segments[0]?.target ?? "unknown",
|
|
18801
19367
|
roleId: input.plan.preview.scope.roleId,
|
|
18802
19368
|
policy: input.plan.policy,
|
|
@@ -18809,7 +19375,7 @@ async function applyTrajectoryImport(input) {
|
|
|
18809
19375
|
const trigger = await enqueueSegmentEvolutionTrigger({
|
|
18810
19376
|
homeDir: input.homeDir,
|
|
18811
19377
|
projectKey,
|
|
18812
|
-
sessionKey,
|
|
19378
|
+
sessionKey: sessionKey2,
|
|
18813
19379
|
runId: null,
|
|
18814
19380
|
roleId: input.plan.preview.scope.roleId,
|
|
18815
19381
|
segmentId: segment.id,
|
|
@@ -18834,7 +19400,7 @@ async function applyTrajectoryImport(input) {
|
|
|
18834
19400
|
await verifyMaterializedSegments({
|
|
18835
19401
|
homeDir: input.homeDir,
|
|
18836
19402
|
projectKey,
|
|
18837
|
-
sessionKey,
|
|
19403
|
+
sessionKey: sessionKey2,
|
|
18838
19404
|
segmentIds: progress.expectedSegmentIds
|
|
18839
19405
|
});
|
|
18840
19406
|
if (progress.completedSegmentIds.length !== progress.expectedSegmentIds.length || progress.completedTriggerIds.length !== progress.expectedTriggerIds.length) {
|
|
@@ -18907,17 +19473,17 @@ async function readCompletedApplyResult(input, manifest) {
|
|
|
18907
19473
|
if (progress.completedSegmentIds.length !== progress.expectedSegmentIds.length || progress.completedTriggerIds.length !== progress.expectedTriggerIds.length) {
|
|
18908
19474
|
throw new Error("Completed trajectory import has incomplete materialization progress.");
|
|
18909
19475
|
}
|
|
18910
|
-
const
|
|
19476
|
+
const sessionKey2 = `historical-${manifest.sourceKey}`;
|
|
18911
19477
|
await verifyMaterializedSegments({
|
|
18912
19478
|
homeDir: input.homeDir,
|
|
18913
19479
|
projectKey,
|
|
18914
|
-
sessionKey,
|
|
19480
|
+
sessionKey: sessionKey2,
|
|
18915
19481
|
segmentIds: progress.expectedSegmentIds
|
|
18916
19482
|
});
|
|
18917
19483
|
const triggerIds = new Set((await listSegmentEvolutionTriggers({
|
|
18918
19484
|
homeDir: input.homeDir,
|
|
18919
19485
|
projectKey
|
|
18920
|
-
})).filter((trigger) => trigger.sessionKey ===
|
|
19486
|
+
})).filter((trigger) => trigger.sessionKey === sessionKey2 && progress.expectedSegmentIds.includes(trigger.segmentId)).map((trigger) => trigger.id));
|
|
18921
19487
|
if (progress.expectedTriggerIds.some((id) => !triggerIds.has(id))) {
|
|
18922
19488
|
throw new Error("Completed trajectory import is missing a materialized trigger.");
|
|
18923
19489
|
}
|
|
@@ -19322,7 +19888,7 @@ __export(exports_review, {
|
|
|
19322
19888
|
validateLearningCandidate: () => validateLearningCandidate,
|
|
19323
19889
|
validateLearningReviewDecisionRecord: () => validateLearningReviewDecisionRecord
|
|
19324
19890
|
});
|
|
19325
|
-
import { mkdir as mkdir15, readFile as
|
|
19891
|
+
import { mkdir as mkdir15, readFile as readFile21, stat as stat11, writeFile as writeFile12 } from "node:fs/promises";
|
|
19326
19892
|
import { dirname as dirname16, join as join23 } from "node:path";
|
|
19327
19893
|
var LEARNING_CANDIDATE_KINDS = [
|
|
19328
19894
|
"lesson",
|
|
@@ -19735,7 +20301,7 @@ function requiredFields(candidate) {
|
|
|
19735
20301
|
];
|
|
19736
20302
|
}
|
|
19737
20303
|
async function parseJsonOrJsonlFile(path, parseItem) {
|
|
19738
|
-
const raw = await
|
|
20304
|
+
const raw = await readFile21(path, "utf8");
|
|
19739
20305
|
if (raw.trim() === "")
|
|
19740
20306
|
return [];
|
|
19741
20307
|
if (path.endsWith(".jsonl")) {
|
|
@@ -19816,7 +20382,7 @@ function isRecord11(value) {
|
|
|
19816
20382
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
19817
20383
|
}
|
|
19818
20384
|
// packages/core/src/pack/index.ts
|
|
19819
|
-
import { readFile as
|
|
20385
|
+
import { readFile as readFile22, readdir as readdir14, stat as stat12 } from "node:fs/promises";
|
|
19820
20386
|
import { isAbsolute as isAbsolute8, join as join24, relative as relative8, sep as sep2 } from "node:path";
|
|
19821
20387
|
|
|
19822
20388
|
// packages/core/src/protected-zones/index.ts
|
|
@@ -19967,7 +20533,7 @@ async function validatePack(packPath) {
|
|
|
19967
20533
|
let assets = [];
|
|
19968
20534
|
let scannedPaths = [];
|
|
19969
20535
|
try {
|
|
19970
|
-
manifest = parsePackManifest(JSON.parse(await
|
|
20536
|
+
manifest = parsePackManifest(JSON.parse(await readFile22(manifestPath, "utf8")));
|
|
19971
20537
|
} catch (error) {
|
|
19972
20538
|
findings.push({
|
|
19973
20539
|
severity: "error",
|
|
@@ -20498,7 +21064,7 @@ function formatError(error) {
|
|
|
20498
21064
|
return error instanceof Error ? error.message : String(error);
|
|
20499
21065
|
}
|
|
20500
21066
|
// packages/core/src/plugins/capabilities.ts
|
|
20501
|
-
import { mkdir as mkdir16, readFile as
|
|
21067
|
+
import { mkdir as mkdir16, readFile as readFile23, writeFile as writeFile13 } from "node:fs/promises";
|
|
20502
21068
|
import { dirname as dirname17, join as join25 } from "node:path";
|
|
20503
21069
|
function createUnknownNegotiatedCapabilities(pluginId) {
|
|
20504
21070
|
return {
|
|
@@ -20621,7 +21187,7 @@ async function writeCodexCapabilityVerificationArtifact(homeDir, artifact) {
|
|
|
20621
21187
|
}
|
|
20622
21188
|
async function readCodexCapabilityVerificationArtifact(homeDir) {
|
|
20623
21189
|
try {
|
|
20624
|
-
const artifact = JSON.parse(await
|
|
21190
|
+
const artifact = JSON.parse(await readFile23(resolveCodexCapabilityArtifactPath(homeDir), "utf8"));
|
|
20625
21191
|
validateCodexCapabilityVerificationArtifact(artifact);
|
|
20626
21192
|
return artifact;
|
|
20627
21193
|
} catch (error) {
|
|
@@ -21248,7 +21814,7 @@ function describeError4(error) {
|
|
|
21248
21814
|
return error instanceof Error ? error.message : String(error);
|
|
21249
21815
|
}
|
|
21250
21816
|
// packages/core/src/workflow/index.ts
|
|
21251
|
-
import { readFile as
|
|
21817
|
+
import { readFile as readFile24, readdir as readdir15 } from "node:fs/promises";
|
|
21252
21818
|
import { join as join27 } from "node:path";
|
|
21253
21819
|
async function scanWorkflowManifests(workflowsDir) {
|
|
21254
21820
|
const manifests = [];
|
|
@@ -21257,7 +21823,7 @@ async function scanWorkflowManifests(workflowsDir) {
|
|
|
21257
21823
|
if (!entry.isDirectory())
|
|
21258
21824
|
continue;
|
|
21259
21825
|
const manifestPath = join27(workflowsDir, entry.name, "WORKFLOW.json");
|
|
21260
|
-
manifests.push(parseWorkflowManifest(JSON.parse(await
|
|
21826
|
+
manifests.push(parseWorkflowManifest(JSON.parse(await readFile24(manifestPath, "utf8"))));
|
|
21261
21827
|
}
|
|
21262
21828
|
return manifests.sort((left, right) => left.id.localeCompare(right.id));
|
|
21263
21829
|
}
|
|
@@ -21466,6 +22032,7 @@ export {
|
|
|
21466
22032
|
hasContextInjectionReceipt,
|
|
21467
22033
|
initializeCoreConfig,
|
|
21468
22034
|
inspectHistoricalSessionEvidenceRetention,
|
|
22035
|
+
inspectSessionEvidenceRetention,
|
|
21469
22036
|
isActiveTeamAgentStatus,
|
|
21470
22037
|
isCapabilityUsable,
|
|
21471
22038
|
isEvolutionImprovementEvalRecommended,
|
|
@@ -21504,6 +22071,7 @@ export {
|
|
|
21504
22071
|
mergeReviewFindings,
|
|
21505
22072
|
mergeSessionMemorySensitivity,
|
|
21506
22073
|
mergeSettings,
|
|
22074
|
+
migrateLegacySessionEvidenceRetention,
|
|
21507
22075
|
negotiatePluginCapabilities,
|
|
21508
22076
|
normalizeHookEvent,
|
|
21509
22077
|
normalizePackPermissions,
|
|
@@ -21533,6 +22101,7 @@ export {
|
|
|
21533
22101
|
previewOkfKnowledgeChangeDecision,
|
|
21534
22102
|
processEvolutionTriggers,
|
|
21535
22103
|
purgeExpiredHistoricalSessionEvidence,
|
|
22104
|
+
purgeExpiredSessionEvidence,
|
|
21536
22105
|
quarantineLegacyGeneratedKnowledge,
|
|
21537
22106
|
queryOkfKnowledge,
|
|
21538
22107
|
queryScopedOkfKnowledgeContext,
|
|
@@ -21569,6 +22138,7 @@ export {
|
|
|
21569
22138
|
rebuildOkfKnowledgeIndexes,
|
|
21570
22139
|
reconcileTeamRun,
|
|
21571
22140
|
recordCodeAgentTraceRefFromHook,
|
|
22141
|
+
recordContextInjectionOutcome,
|
|
21572
22142
|
recordDiscoveredProject,
|
|
21573
22143
|
recordTeamAgentNativeSession,
|
|
21574
22144
|
redactSessionMemoryCredentialText,
|
|
@@ -21578,6 +22148,7 @@ export {
|
|
|
21578
22148
|
renderMainTeamOverlayContext,
|
|
21579
22149
|
renderRoleAgentDefinitionContext,
|
|
21580
22150
|
replaceOkfKnowledgeChangeCandidate,
|
|
22151
|
+
resetSessionRawEvents,
|
|
21581
22152
|
resolveCodeAgentTraceRefPaths,
|
|
21582
22153
|
resolveCodexCapabilityArtifactPath,
|
|
21583
22154
|
resolveContextInjectionReceiptPath,
|
|
@@ -21591,6 +22162,7 @@ export {
|
|
|
21591
22162
|
resolveLearningDecisionPath,
|
|
21592
22163
|
resolveObservabilityStorePaths,
|
|
21593
22164
|
resolveOkfKnowledgePaths,
|
|
22165
|
+
resolveOkfKnowledgeRuntimeEligibility,
|
|
21594
22166
|
resolvePathProjectLogKey,
|
|
21595
22167
|
resolveProjectLogIdentity,
|
|
21596
22168
|
resolveProjectLogKey,
|
|
@@ -21609,6 +22181,7 @@ export {
|
|
|
21609
22181
|
resumeFailedOkfKnowledgePlan,
|
|
21610
22182
|
resumeTeamRun,
|
|
21611
22183
|
revokeOkfKnowledgeConcept,
|
|
22184
|
+
rewriteSessionRawEvents,
|
|
21612
22185
|
runDaemonForeground,
|
|
21613
22186
|
runPluginConformance,
|
|
21614
22187
|
runSync,
|