@evo-dev/evodev 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/.claude-plugin/marketplace.json +2 -2
- package/dist/index.js +958 -301
- package/dist/plugins/evodev/.claude-plugin/plugin.json +1 -1
- package/dist/plugins/evodev/.codex-plugin/plugin.json +1 -1
- package/dist/plugins/evodev/package.json +1 -1
- package/dist/ui/app.js +5 -5
- package/dist/ui/styles.css +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -229,12 +229,12 @@ import { resolve as resolve15 } from "node:path";
|
|
|
229
229
|
import { fileURLToPath as fileURLToPath5 } from "node:url";
|
|
230
230
|
|
|
231
231
|
// packages/core/src/agents/index.ts
|
|
232
|
-
import { readFile as
|
|
232
|
+
import { readFile as readFile12 } from "node:fs/promises";
|
|
233
233
|
|
|
234
234
|
// packages/core/src/evolution/knowledge/index.ts
|
|
235
235
|
import { createHash as createHash5 } from "node:crypto";
|
|
236
236
|
import { existsSync, readFileSync as readFileSync2, readdirSync } from "node:fs";
|
|
237
|
-
import { mkdir as mkdir8, readFile as
|
|
237
|
+
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";
|
|
238
238
|
import { dirname as dirname9, isAbsolute as isAbsolute5, join as join13, relative as relative4, resolve as resolve4 } from "node:path";
|
|
239
239
|
|
|
240
240
|
// packages/core/src/config/paths.ts
|
|
@@ -285,7 +285,7 @@ function stripTrailingSlash(path) {
|
|
|
285
285
|
}
|
|
286
286
|
|
|
287
287
|
// packages/core/src/config/settings.ts
|
|
288
|
-
import { readFile as
|
|
288
|
+
import { readFile as readFile9 } from "node:fs/promises";
|
|
289
289
|
|
|
290
290
|
// packages/core/src/utils/errors.ts
|
|
291
291
|
function isNotFoundError(error) {
|
|
@@ -2072,6 +2072,9 @@ function asRecord(value) {
|
|
|
2072
2072
|
function createSemanticPacketMessageKey(value) {
|
|
2073
2073
|
return sha256Hex(value);
|
|
2074
2074
|
}
|
|
2075
|
+
// packages/core/src/evolution/evidence/session-memory/retention.ts
|
|
2076
|
+
import { readFile as readFile5 } from "node:fs/promises";
|
|
2077
|
+
|
|
2075
2078
|
// packages/core/src/evolution/candidates/index.ts
|
|
2076
2079
|
import { createHash as createHash2 } from "node:crypto";
|
|
2077
2080
|
import { mkdir as mkdir2, readFile as readFile2, readdir as readdir3, rm, writeFile as writeFile2 } from "node:fs/promises";
|
|
@@ -2840,6 +2843,36 @@ async function appendRawEvent(path, event) {
|
|
|
2840
2843
|
`, "utf8");
|
|
2841
2844
|
return { lineNumber: existingLineCount + 1 };
|
|
2842
2845
|
}
|
|
2846
|
+
async function resetSessionRawEvents(path) {
|
|
2847
|
+
await mkdir3(dirname3(path), { recursive: true, mode: 448 });
|
|
2848
|
+
await writeFile3(path, "", { encoding: "utf8", mode: 384 });
|
|
2849
|
+
await chmod(path, 384);
|
|
2850
|
+
}
|
|
2851
|
+
async function rewriteSessionRawEvents(path, lines, expectedCurrent) {
|
|
2852
|
+
await ensurePrivateSessionMemoryDirectory(dirname3(path));
|
|
2853
|
+
const temporaryPath = join6(dirname3(path), `.${randomUUID()}.tmp`);
|
|
2854
|
+
try {
|
|
2855
|
+
const handle = await open(temporaryPath, "wx", 384);
|
|
2856
|
+
try {
|
|
2857
|
+
await handle.writeFile(lines.length === 0 ? "" : `${lines.join(`
|
|
2858
|
+
`)}
|
|
2859
|
+
`, "utf8");
|
|
2860
|
+
await handle.sync();
|
|
2861
|
+
} finally {
|
|
2862
|
+
await handle.close();
|
|
2863
|
+
}
|
|
2864
|
+
const current = await readFile4(path, "utf8").catch(() => null);
|
|
2865
|
+
if (current !== expectedCurrent)
|
|
2866
|
+
return false;
|
|
2867
|
+
await rename(temporaryPath, path);
|
|
2868
|
+
await chmod(path, 384);
|
|
2869
|
+
return true;
|
|
2870
|
+
} finally {
|
|
2871
|
+
await rm2(temporaryPath, { force: true }).catch(() => {
|
|
2872
|
+
return;
|
|
2873
|
+
});
|
|
2874
|
+
}
|
|
2875
|
+
}
|
|
2843
2876
|
async function readLineRange(path, fromLine, toLine) {
|
|
2844
2877
|
const text2 = await readFile4(path, "utf8");
|
|
2845
2878
|
const lines = text2.split(`
|
|
@@ -2995,7 +3028,7 @@ async function writeHistoricalSessionStateAndIndex(input) {
|
|
|
2995
3028
|
return { state, segmentIds: ordered.map((segment) => segment.id) };
|
|
2996
3029
|
}
|
|
2997
3030
|
async function writeSessionEvidenceSegmentAtomic(input) {
|
|
2998
|
-
|
|
3031
|
+
assertSessionEvidenceRetentionState(input.segment);
|
|
2999
3032
|
const paths = resolveSessionMemoryPaths({
|
|
3000
3033
|
homeDir: input.homeDir,
|
|
3001
3034
|
projectKey: input.segment.projectKey,
|
|
@@ -3003,18 +3036,18 @@ async function writeSessionEvidenceSegmentAtomic(input) {
|
|
|
3003
3036
|
});
|
|
3004
3037
|
await writePrivateAtomicJson(paths.segmentPath(input.segment.id), input.segment);
|
|
3005
3038
|
}
|
|
3006
|
-
function
|
|
3007
|
-
if (segment.
|
|
3008
|
-
throw new Error("Atomic Session Evidence rewrite
|
|
3039
|
+
function assertSessionEvidenceRetentionState(segment) {
|
|
3040
|
+
if (segment.retention === undefined) {
|
|
3041
|
+
throw new Error("Atomic Session Evidence rewrite requires retention metadata.");
|
|
3009
3042
|
}
|
|
3010
3043
|
if (segment.retention.rawState === "available") {
|
|
3011
3044
|
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) {
|
|
3012
|
-
throw new Error("Available
|
|
3045
|
+
throw new Error("Available Session Evidence raw state is invalid.");
|
|
3013
3046
|
}
|
|
3014
3047
|
return;
|
|
3015
3048
|
}
|
|
3016
3049
|
if (segment.rawExcerpt.stored || segment.rawExcerpt.content !== "" || segment.rawExcerpt.byteLength !== 0 || segment.rawExcerpt.sha256 !== sha256Hex("") || segment.retention.rawPurgedAt === null || segment.lifecycle.status !== "raw-expired") {
|
|
3017
|
-
throw new Error("Purged
|
|
3050
|
+
throw new Error("Purged Session Evidence raw state is invalid.");
|
|
3018
3051
|
}
|
|
3019
3052
|
}
|
|
3020
3053
|
async function writeJson(path, value) {
|
|
@@ -3231,69 +3264,89 @@ async function listJsonFiles(path) {
|
|
|
3231
3264
|
|
|
3232
3265
|
// packages/core/src/evolution/evidence/session-memory/retention.ts
|
|
3233
3266
|
var DEFAULT_RETENTION_LIMIT = 20;
|
|
3234
|
-
var MAX_RETENTION_LIMIT =
|
|
3235
|
-
|
|
3267
|
+
var MAX_RETENTION_LIMIT = 1e4;
|
|
3268
|
+
var DEFAULT_LEGACY_RETENTION_DAYS = 30;
|
|
3269
|
+
async function inspectSessionEvidenceRetention(input) {
|
|
3270
|
+
const result = await inspectRetention(input, () => true);
|
|
3271
|
+
const expiredEvents = await expireCapturedRawEvents({ ...input, apply: false });
|
|
3272
|
+
result.expiredCapturedEventPayloads = expiredEvents.payloads;
|
|
3273
|
+
result.expiredCapturedEventRawBytes = expiredEvents.rawBytes;
|
|
3274
|
+
return result;
|
|
3275
|
+
}
|
|
3276
|
+
async function inspectRetention(input, include) {
|
|
3236
3277
|
const now = normalizeTimestamp(input.now);
|
|
3237
|
-
const
|
|
3278
|
+
const segments = (await listSessionEvidenceSegments({
|
|
3238
3279
|
homeDir: input.homeDir,
|
|
3239
3280
|
projectKey: input.projectKey
|
|
3240
|
-
})).filter(
|
|
3241
|
-
const
|
|
3242
|
-
|
|
3243
|
-
|
|
3244
|
-
|
|
3245
|
-
|
|
3246
|
-
|
|
3247
|
-
|
|
3248
|
-
|
|
3249
|
-
|
|
3250
|
-
|
|
3251
|
-
|
|
3281
|
+
})).filter(include);
|
|
3282
|
+
const retentionDays = await loadLegacyRetentionDays(input.homeDir, input.projectKey);
|
|
3283
|
+
const result = emptyInspection();
|
|
3284
|
+
const context = createRetentionContext();
|
|
3285
|
+
for (const segment of segments) {
|
|
3286
|
+
if (segment.retention === undefined) {
|
|
3287
|
+
const rawBytes = segment.rawExcerpt.stored ? segment.rawExcerpt.byteLength : 0;
|
|
3288
|
+
result.legacyWithoutRetention += 1;
|
|
3289
|
+
result.legacyRawBytes += rawBytes;
|
|
3290
|
+
const expiresAt = legacyExpiresAt(segment, retentionDays);
|
|
3291
|
+
if (Date.parse(expiresAt) <= Date.parse(now)) {
|
|
3292
|
+
result.legacyOverdue += 1;
|
|
3293
|
+
result.legacyOverdueRawBytes += rawBytes;
|
|
3294
|
+
const protection2 = await findRetentionProtection({
|
|
3295
|
+
homeDir: input.homeDir,
|
|
3296
|
+
segment,
|
|
3297
|
+
context
|
|
3298
|
+
});
|
|
3299
|
+
if (protection2 === null)
|
|
3300
|
+
result.purgeEligible += 1;
|
|
3301
|
+
else
|
|
3302
|
+
addProtection(result, protection2);
|
|
3303
|
+
}
|
|
3304
|
+
continue;
|
|
3305
|
+
}
|
|
3306
|
+
if (segment.retention.rawState === "purged") {
|
|
3252
3307
|
result.purged += 1;
|
|
3253
3308
|
continue;
|
|
3254
3309
|
}
|
|
3255
3310
|
result.available += 1;
|
|
3256
3311
|
if (!hasValidAvailableRetention(segment)) {
|
|
3257
3312
|
result.overdue += 1;
|
|
3258
|
-
result
|
|
3259
|
-
result.protectionReasons["retention-invalid"] = (result.protectionReasons["retention-invalid"] ?? 0) + 1;
|
|
3313
|
+
addProtection(result, "retention-invalid");
|
|
3260
3314
|
continue;
|
|
3261
3315
|
}
|
|
3262
|
-
if (
|
|
3316
|
+
if (Date.parse(segment.retention.expiresAt) > Date.parse(now))
|
|
3263
3317
|
continue;
|
|
3264
|
-
}
|
|
3265
3318
|
result.overdue += 1;
|
|
3266
3319
|
const protection = await findRetentionProtection({
|
|
3267
3320
|
homeDir: input.homeDir,
|
|
3268
3321
|
segment,
|
|
3269
|
-
|
|
3322
|
+
context
|
|
3270
3323
|
});
|
|
3271
|
-
if (protection === null)
|
|
3324
|
+
if (protection === null)
|
|
3272
3325
|
result.purgeEligible += 1;
|
|
3273
|
-
|
|
3274
|
-
|
|
3275
|
-
result.protected += 1;
|
|
3276
|
-
result.protectionReasons[protection] = (result.protectionReasons[protection] ?? 0) + 1;
|
|
3326
|
+
else
|
|
3327
|
+
addProtection(result, protection);
|
|
3277
3328
|
}
|
|
3278
3329
|
return result;
|
|
3279
3330
|
}
|
|
3280
|
-
async function
|
|
3331
|
+
async function purgeExpiredSessionEvidence(input) {
|
|
3332
|
+
return purgeRetention(input, () => true);
|
|
3333
|
+
}
|
|
3334
|
+
async function purgeRetention(input, include) {
|
|
3281
3335
|
const now = normalizeTimestamp(input.now);
|
|
3282
3336
|
const limit = normalizeLimit(input.limit);
|
|
3283
|
-
const
|
|
3337
|
+
const segments = (await listSessionEvidenceSegments({
|
|
3284
3338
|
homeDir: input.homeDir,
|
|
3285
3339
|
projectKey: input.projectKey
|
|
3286
|
-
})).filter((segment) => segment
|
|
3340
|
+
})).filter((segment) => include(segment) && segment.retention !== undefined).sort((left, right) => (left.retention?.expiresAt ?? "").localeCompare(right.retention?.expiresAt ?? "") || left.id.localeCompare(right.id));
|
|
3287
3341
|
const result = {
|
|
3288
3342
|
scanned: 0,
|
|
3289
3343
|
due: 0,
|
|
3290
3344
|
purged: 0,
|
|
3291
|
-
alreadyPurged:
|
|
3345
|
+
alreadyPurged: segments.filter((segment) => segment.retention?.rawState === "purged").length,
|
|
3292
3346
|
protected: []
|
|
3293
3347
|
};
|
|
3294
|
-
const
|
|
3295
|
-
|
|
3296
|
-
for (const segment of historical) {
|
|
3348
|
+
const context = createRetentionContext();
|
|
3349
|
+
for (const segment of segments) {
|
|
3297
3350
|
if (result.scanned >= limit)
|
|
3298
3351
|
break;
|
|
3299
3352
|
if (segment.retention === undefined || segment.retention.rawState === "purged")
|
|
@@ -3314,7 +3367,7 @@ async function purgeExpiredHistoricalSessionEvidence(input) {
|
|
|
3314
3367
|
const protection = await findRetentionProtection({
|
|
3315
3368
|
homeDir: input.homeDir,
|
|
3316
3369
|
segment,
|
|
3317
|
-
|
|
3370
|
+
context
|
|
3318
3371
|
});
|
|
3319
3372
|
if (protection !== null) {
|
|
3320
3373
|
result.protected.push({
|
|
@@ -3330,8 +3383,7 @@ async function purgeExpiredHistoricalSessionEvidence(input) {
|
|
|
3330
3383
|
sessionKey: segment.sessionKey,
|
|
3331
3384
|
segmentId: segment.id
|
|
3332
3385
|
});
|
|
3333
|
-
|
|
3334
|
-
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)) {
|
|
3386
|
+
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) {
|
|
3335
3387
|
result.protected.push({
|
|
3336
3388
|
projectKey: segment.projectKey,
|
|
3337
3389
|
segmentId: segment.id,
|
|
@@ -3339,30 +3391,115 @@ async function purgeExpiredHistoricalSessionEvidence(input) {
|
|
|
3339
3391
|
});
|
|
3340
3392
|
continue;
|
|
3341
3393
|
}
|
|
3342
|
-
const
|
|
3394
|
+
const freshProtection = await findRetentionProtection({
|
|
3343
3395
|
homeDir: input.homeDir,
|
|
3344
|
-
|
|
3396
|
+
segment: current,
|
|
3397
|
+
context: createRetentionContext()
|
|
3345
3398
|
});
|
|
3346
|
-
if (
|
|
3399
|
+
if (freshProtection !== null) {
|
|
3347
3400
|
result.protected.push({
|
|
3348
|
-
projectKey:
|
|
3349
|
-
segmentId:
|
|
3350
|
-
reason:
|
|
3401
|
+
projectKey: current.projectKey,
|
|
3402
|
+
segmentId: current.id,
|
|
3403
|
+
reason: freshProtection
|
|
3351
3404
|
});
|
|
3352
3405
|
continue;
|
|
3353
3406
|
}
|
|
3354
3407
|
await writeSessionEvidenceSegmentAtomic({
|
|
3355
3408
|
homeDir: input.homeDir,
|
|
3356
|
-
segment:
|
|
3409
|
+
segment: createPurgedSegment(current, now)
|
|
3357
3410
|
});
|
|
3358
3411
|
result.purged += 1;
|
|
3359
3412
|
}
|
|
3360
3413
|
return result;
|
|
3361
3414
|
}
|
|
3415
|
+
async function migrateLegacySessionEvidenceRetention(input) {
|
|
3416
|
+
const now = normalizeTimestamp(input.now);
|
|
3417
|
+
const retentionDays = await loadLegacyRetentionDays(input.homeDir, input.projectKey);
|
|
3418
|
+
const segments = await listSessionEvidenceSegments({
|
|
3419
|
+
homeDir: input.homeDir,
|
|
3420
|
+
projectKey: input.projectKey
|
|
3421
|
+
});
|
|
3422
|
+
let migrated = 0;
|
|
3423
|
+
const context = createRetentionContext();
|
|
3424
|
+
for (const segment of segments) {
|
|
3425
|
+
if (segment.retention !== undefined || !segment.rawExcerpt.stored)
|
|
3426
|
+
continue;
|
|
3427
|
+
const policyDays = retentionDays.get(sessionKey(segment)) ?? DEFAULT_LEGACY_RETENTION_DAYS;
|
|
3428
|
+
if (Date.parse(addDays(segment.createdAt, policyDays)) > Date.parse(now))
|
|
3429
|
+
continue;
|
|
3430
|
+
const protection = await findRetentionProtection({
|
|
3431
|
+
homeDir: input.homeDir,
|
|
3432
|
+
segment,
|
|
3433
|
+
context
|
|
3434
|
+
});
|
|
3435
|
+
if (protection !== null)
|
|
3436
|
+
continue;
|
|
3437
|
+
const current = await readSessionEvidenceSegment({
|
|
3438
|
+
homeDir: input.homeDir,
|
|
3439
|
+
projectKey: segment.projectKey,
|
|
3440
|
+
sessionKey: segment.sessionKey,
|
|
3441
|
+
segmentId: segment.id
|
|
3442
|
+
});
|
|
3443
|
+
if (current.retention !== undefined || JSON.stringify(current) !== JSON.stringify(segment)) {
|
|
3444
|
+
continue;
|
|
3445
|
+
}
|
|
3446
|
+
await writeSessionEvidenceSegmentAtomic({
|
|
3447
|
+
homeDir: input.homeDir,
|
|
3448
|
+
segment: {
|
|
3449
|
+
...current,
|
|
3450
|
+
retention: {
|
|
3451
|
+
policyDays,
|
|
3452
|
+
expiresAt: addDays(current.createdAt, policyDays),
|
|
3453
|
+
rawState: "available",
|
|
3454
|
+
rawPurgedAt: null,
|
|
3455
|
+
originalRawSha256: current.rawExcerpt.sha256
|
|
3456
|
+
}
|
|
3457
|
+
}
|
|
3458
|
+
});
|
|
3459
|
+
migrated += 1;
|
|
3460
|
+
}
|
|
3461
|
+
const expiredEvents = await expireCapturedRawEvents({
|
|
3462
|
+
homeDir: input.homeDir,
|
|
3463
|
+
projectKey: input.projectKey,
|
|
3464
|
+
now,
|
|
3465
|
+
apply: true
|
|
3466
|
+
});
|
|
3467
|
+
return {
|
|
3468
|
+
migrated,
|
|
3469
|
+
expiredCapturedEventPayloads: expiredEvents.payloads,
|
|
3470
|
+
expiredCapturedEventRawBytes: expiredEvents.rawBytes,
|
|
3471
|
+
purge: await purgeExpiredSessionEvidence({
|
|
3472
|
+
homeDir: input.homeDir,
|
|
3473
|
+
projectKey: input.projectKey,
|
|
3474
|
+
now,
|
|
3475
|
+
limit: Math.max(DEFAULT_RETENTION_LIMIT, segments.length)
|
|
3476
|
+
})
|
|
3477
|
+
};
|
|
3478
|
+
}
|
|
3479
|
+
function emptyInspection() {
|
|
3480
|
+
return {
|
|
3481
|
+
available: 0,
|
|
3482
|
+
purged: 0,
|
|
3483
|
+
overdue: 0,
|
|
3484
|
+
purgeEligible: 0,
|
|
3485
|
+
protected: 0,
|
|
3486
|
+
legacyWithoutRetention: 0,
|
|
3487
|
+
legacyOverdue: 0,
|
|
3488
|
+
legacyRawBytes: 0,
|
|
3489
|
+
legacyOverdueRawBytes: 0,
|
|
3490
|
+
expiredCapturedEventPayloads: 0,
|
|
3491
|
+
expiredCapturedEventRawBytes: 0,
|
|
3492
|
+
protectionReasons: {}
|
|
3493
|
+
};
|
|
3494
|
+
}
|
|
3495
|
+
function addProtection(result, reason) {
|
|
3496
|
+
result.protected += 1;
|
|
3497
|
+
result.protectionReasons[reason] = (result.protectionReasons[reason] ?? 0) + 1;
|
|
3498
|
+
}
|
|
3362
3499
|
function normalizeLimit(value) {
|
|
3363
3500
|
const limit = value ?? DEFAULT_RETENTION_LIMIT;
|
|
3364
3501
|
if (!Number.isSafeInteger(limit) || limit < 1 || limit > MAX_RETENTION_LIMIT) {
|
|
3365
|
-
throw new Error(`
|
|
3502
|
+
throw new Error(`Session Evidence retention limit must be between 1 and ${MAX_RETENTION_LIMIT}.`);
|
|
3366
3503
|
}
|
|
3367
3504
|
return limit;
|
|
3368
3505
|
}
|
|
@@ -3374,26 +3511,38 @@ async function findRetentionProtection(input) {
|
|
|
3374
3511
|
if (input.segment.lifecycle.reviewState === "unreviewed" || input.segment.lifecycle.reviewState === "deferred") {
|
|
3375
3512
|
return "segment-review-pending";
|
|
3376
3513
|
}
|
|
3377
|
-
const trigger = await findSegmentTrigger(input.homeDir, input.segment);
|
|
3378
|
-
if (trigger === null)
|
|
3514
|
+
const trigger = await findSegmentTrigger(input.homeDir, input.segment, input.context);
|
|
3515
|
+
if (trigger === null) {
|
|
3516
|
+
if (input.segment.reason === "session-memory-threshold")
|
|
3517
|
+
return null;
|
|
3379
3518
|
return "trigger-missing";
|
|
3519
|
+
}
|
|
3380
3520
|
if (!isTerminalTrigger(trigger))
|
|
3381
3521
|
return "trigger-not-terminal";
|
|
3382
|
-
let snapshot = input.reviewSnapshots.get(input.segment.projectKey);
|
|
3522
|
+
let snapshot = input.context.reviewSnapshots.get(input.segment.projectKey);
|
|
3383
3523
|
if (snapshot === undefined) {
|
|
3384
3524
|
snapshot = readEvolutionReviewSnapshot({
|
|
3385
3525
|
homeDir: input.homeDir,
|
|
3386
3526
|
projectKey: input.segment.projectKey
|
|
3387
3527
|
});
|
|
3388
|
-
input.reviewSnapshots.set(input.segment.projectKey, snapshot);
|
|
3528
|
+
input.context.reviewSnapshots.set(input.segment.projectKey, snapshot);
|
|
3389
3529
|
}
|
|
3390
3530
|
return hasPendingDerivedReview(await snapshot, input.segment) ? "derived-review-pending" : null;
|
|
3391
3531
|
}
|
|
3392
|
-
async function findSegmentTrigger(homeDir, segment) {
|
|
3393
|
-
|
|
3394
|
-
|
|
3395
|
-
projectKey: segment.projectKey
|
|
3396
|
-
|
|
3532
|
+
async function findSegmentTrigger(homeDir, segment, context) {
|
|
3533
|
+
let triggerMap = context.triggerMaps.get(segment.projectKey);
|
|
3534
|
+
if (triggerMap === undefined) {
|
|
3535
|
+
triggerMap = listSegmentEvolutionTriggers({ homeDir, projectKey: segment.projectKey }).then((triggers) => {
|
|
3536
|
+
const map = new Map;
|
|
3537
|
+
for (const trigger of triggers) {
|
|
3538
|
+
const key = `${trigger.sessionKey}\x00${trigger.segmentId}`;
|
|
3539
|
+
map.set(key, [...map.get(key) ?? [], trigger]);
|
|
3540
|
+
}
|
|
3541
|
+
return map;
|
|
3542
|
+
});
|
|
3543
|
+
context.triggerMaps.set(segment.projectKey, triggerMap);
|
|
3544
|
+
}
|
|
3545
|
+
const matches = (await triggerMap).get(`${segment.sessionKey}\x00${segment.id}`) ?? [];
|
|
3397
3546
|
return matches.length === 1 ? matches[0] ?? null : null;
|
|
3398
3547
|
}
|
|
3399
3548
|
function isTerminalTrigger(trigger) {
|
|
@@ -3412,10 +3561,10 @@ function hasPendingDerivedReview(snapshot, segment) {
|
|
|
3412
3561
|
}
|
|
3413
3562
|
return snapshot.reviewCandidates.some((candidate) => candidate.runId === runId && candidate.reviewState !== "accepted" && candidate.reviewState !== "rejected");
|
|
3414
3563
|
}
|
|
3415
|
-
function
|
|
3564
|
+
function createPurgedSegment(segment, now) {
|
|
3416
3565
|
const retention = segment.retention;
|
|
3417
3566
|
if (retention === undefined || retention.rawState !== "available") {
|
|
3418
|
-
throw new Error("
|
|
3567
|
+
throw new Error("Session Evidence raw body is not available for purge.");
|
|
3419
3568
|
}
|
|
3420
3569
|
return {
|
|
3421
3570
|
...segment,
|
|
@@ -3443,11 +3592,109 @@ function createPurgedHistoricalSegment(segment, now) {
|
|
|
3443
3592
|
}
|
|
3444
3593
|
};
|
|
3445
3594
|
}
|
|
3595
|
+
function createRetentionContext() {
|
|
3596
|
+
return {
|
|
3597
|
+
reviewSnapshots: new Map,
|
|
3598
|
+
triggerMaps: new Map
|
|
3599
|
+
};
|
|
3600
|
+
}
|
|
3601
|
+
async function loadLegacyRetentionDays(homeDir, projectKey) {
|
|
3602
|
+
const states = await listSessionMemoryStates({ homeDir, projectKey });
|
|
3603
|
+
return new Map(states.map((state) => [`${state.projectKey}\x00${state.sessionKey}`, state.policy.retentionDays]));
|
|
3604
|
+
}
|
|
3605
|
+
function legacyExpiresAt(segment, retentionDays) {
|
|
3606
|
+
return addDays(segment.createdAt, retentionDays.get(sessionKey(segment)) ?? DEFAULT_LEGACY_RETENTION_DAYS);
|
|
3607
|
+
}
|
|
3608
|
+
function sessionKey(segment) {
|
|
3609
|
+
return `${segment.projectKey}\x00${segment.sessionKey}`;
|
|
3610
|
+
}
|
|
3611
|
+
function addDays(value, days) {
|
|
3612
|
+
const timestamp = Date.parse(value);
|
|
3613
|
+
if (!Number.isFinite(timestamp))
|
|
3614
|
+
throw new Error("Invalid Session Evidence retention timestamp.");
|
|
3615
|
+
return new Date(timestamp + days * 24 * 60 * 60 * 1000).toISOString();
|
|
3616
|
+
}
|
|
3617
|
+
async function expireCapturedRawEvents(input) {
|
|
3618
|
+
const now = Date.parse(normalizeTimestamp(input.now));
|
|
3619
|
+
const states = await listSessionMemoryStates({
|
|
3620
|
+
homeDir: input.homeDir,
|
|
3621
|
+
projectKey: input.projectKey
|
|
3622
|
+
});
|
|
3623
|
+
let payloads = 0;
|
|
3624
|
+
let rawBytes = 0;
|
|
3625
|
+
for (const state of states) {
|
|
3626
|
+
const stateUpdatedAt = Date.parse(state.updatedAt);
|
|
3627
|
+
if (Number.isFinite(stateUpdatedAt) && now - stateUpdatedAt < 5 * 60 * 1000)
|
|
3628
|
+
continue;
|
|
3629
|
+
const paths = resolveSessionMemoryPaths({
|
|
3630
|
+
homeDir: input.homeDir,
|
|
3631
|
+
projectKey: state.projectKey,
|
|
3632
|
+
sessionKey: state.sessionKey
|
|
3633
|
+
});
|
|
3634
|
+
const cursor = await readCursor(paths.cursorPath);
|
|
3635
|
+
const capturedLine = cursor?.lastCapturedLine ?? 0;
|
|
3636
|
+
if (capturedLine <= 0)
|
|
3637
|
+
continue;
|
|
3638
|
+
let text2;
|
|
3639
|
+
try {
|
|
3640
|
+
text2 = await readFile5(paths.eventsPath, "utf8");
|
|
3641
|
+
} catch {
|
|
3642
|
+
continue;
|
|
3643
|
+
}
|
|
3644
|
+
const lines = text2.split(`
|
|
3645
|
+
`).filter((line) => line.trim() !== "");
|
|
3646
|
+
let changed = false;
|
|
3647
|
+
let filePayloads = 0;
|
|
3648
|
+
let fileRawBytes = 0;
|
|
3649
|
+
const nextLines = lines.map((line, index) => {
|
|
3650
|
+
if (index >= capturedLine)
|
|
3651
|
+
return line;
|
|
3652
|
+
let event;
|
|
3653
|
+
try {
|
|
3654
|
+
event = JSON.parse(line);
|
|
3655
|
+
} catch {
|
|
3656
|
+
return line;
|
|
3657
|
+
}
|
|
3658
|
+
const expiresAt = Date.parse(event.receivedAt) + state.policy.retentionDays * 24 * 60 * 60 * 1000;
|
|
3659
|
+
if (!Number.isFinite(expiresAt) || expiresAt > now || event.rawPayloadExpired === true || event.rawPayloadJson === "{}") {
|
|
3660
|
+
return line;
|
|
3661
|
+
}
|
|
3662
|
+
filePayloads += 1;
|
|
3663
|
+
fileRawBytes += Buffer.byteLength(event.rawPayloadJson, "utf8");
|
|
3664
|
+
if (!input.apply)
|
|
3665
|
+
return line;
|
|
3666
|
+
changed = true;
|
|
3667
|
+
return JSON.stringify({
|
|
3668
|
+
...event,
|
|
3669
|
+
rawPayloadJson: "{}",
|
|
3670
|
+
rawPayloadTruncated: false,
|
|
3671
|
+
rawPayloadExpired: true
|
|
3672
|
+
});
|
|
3673
|
+
});
|
|
3674
|
+
if (!input.apply) {
|
|
3675
|
+
payloads += filePayloads;
|
|
3676
|
+
rawBytes += fileRawBytes;
|
|
3677
|
+
continue;
|
|
3678
|
+
}
|
|
3679
|
+
if (changed && await rewriteSessionRawEvents(paths.eventsPath, nextLines, text2)) {
|
|
3680
|
+
payloads += filePayloads;
|
|
3681
|
+
rawBytes += fileRawBytes;
|
|
3682
|
+
}
|
|
3683
|
+
}
|
|
3684
|
+
return { payloads, rawBytes };
|
|
3685
|
+
}
|
|
3686
|
+
async function readCursor(path) {
|
|
3687
|
+
try {
|
|
3688
|
+
return JSON.parse(await readFile5(path, "utf8"));
|
|
3689
|
+
} catch {
|
|
3690
|
+
return null;
|
|
3691
|
+
}
|
|
3692
|
+
}
|
|
3446
3693
|
// packages/core/src/evolution/evidence/session-memory/updater.ts
|
|
3447
3694
|
import { join as join9 } from "node:path";
|
|
3448
3695
|
|
|
3449
3696
|
// packages/core/src/projects/index.ts
|
|
3450
|
-
import { lstat as lstat2, readFile as
|
|
3697
|
+
import { lstat as lstat2, readFile as readFile6, readdir as readdir5, realpath, stat as stat2 } from "node:fs/promises";
|
|
3451
3698
|
import { basename as basename2, dirname as dirname5, isAbsolute as isAbsolute3, join as join8, parse } from "node:path";
|
|
3452
3699
|
|
|
3453
3700
|
// packages/core/src/runtime-logs/index.ts
|
|
@@ -3521,7 +3768,7 @@ function createDebugLogEntry(input) {
|
|
|
3521
3768
|
}
|
|
3522
3769
|
function createEvoDevExecutionEvent(input) {
|
|
3523
3770
|
const timestamp = normalizeTimestamp(input.timestamp);
|
|
3524
|
-
const
|
|
3771
|
+
const sessionKey2 = sanitizePersistentIdentifier(input.sessionKey, "session");
|
|
3525
3772
|
const target = normalizeExecutionEventTarget(input.target);
|
|
3526
3773
|
const eventType = sanitizeEventText(input.eventType, "unknown");
|
|
3527
3774
|
const projectKey = input.projectKey === undefined || input.projectKey === null ? null : sanitizePersistentIdentifier(input.projectKey, "project");
|
|
@@ -3542,7 +3789,7 @@ function createEvoDevExecutionEvent(input) {
|
|
|
3542
3789
|
timestamp,
|
|
3543
3790
|
target,
|
|
3544
3791
|
eventType,
|
|
3545
|
-
sessionKey,
|
|
3792
|
+
sessionKey: sessionKey2,
|
|
3546
3793
|
projectKey,
|
|
3547
3794
|
runId,
|
|
3548
3795
|
roleId,
|
|
@@ -3558,7 +3805,7 @@ function createEvoDevExecutionEvent(input) {
|
|
|
3558
3805
|
runId,
|
|
3559
3806
|
roleId,
|
|
3560
3807
|
taskId,
|
|
3561
|
-
sessionKey,
|
|
3808
|
+
sessionKey: sessionKey2,
|
|
3562
3809
|
summary,
|
|
3563
3810
|
metadata,
|
|
3564
3811
|
decision,
|
|
@@ -4333,7 +4580,7 @@ async function findNearestGitRoot(cwd) {
|
|
|
4333
4580
|
}
|
|
4334
4581
|
async function readDiscoveredProject(path) {
|
|
4335
4582
|
try {
|
|
4336
|
-
return parseDiscoveredProject(JSON.parse(await
|
|
4583
|
+
return parseDiscoveredProject(JSON.parse(await readFile6(path, "utf8")));
|
|
4337
4584
|
} catch (error) {
|
|
4338
4585
|
if (isNotFoundError(error) || error instanceof ProjectRegistrationError)
|
|
4339
4586
|
return null;
|
|
@@ -4342,7 +4589,7 @@ async function readDiscoveredProject(path) {
|
|
|
4342
4589
|
}
|
|
4343
4590
|
async function readRegisteredProject(path) {
|
|
4344
4591
|
try {
|
|
4345
|
-
return parseRegisteredProject(JSON.parse(await
|
|
4592
|
+
return parseRegisteredProject(JSON.parse(await readFile6(path, "utf8")));
|
|
4346
4593
|
} catch (error) {
|
|
4347
4594
|
if (isNotFoundError(error) || error instanceof ProjectRegistrationError)
|
|
4348
4595
|
return null;
|
|
@@ -4351,7 +4598,7 @@ async function readRegisteredProject(path) {
|
|
|
4351
4598
|
}
|
|
4352
4599
|
async function readProjectWorkspace(path) {
|
|
4353
4600
|
try {
|
|
4354
|
-
return parseProjectWorkspace(JSON.parse(await
|
|
4601
|
+
return parseProjectWorkspace(JSON.parse(await readFile6(path, "utf8")));
|
|
4355
4602
|
} catch (error) {
|
|
4356
4603
|
if (isNotFoundError(error) || error instanceof ProjectRegistrationError)
|
|
4357
4604
|
return null;
|
|
@@ -4360,7 +4607,7 @@ async function readProjectWorkspace(path) {
|
|
|
4360
4607
|
}
|
|
4361
4608
|
async function readProjectAlias(path) {
|
|
4362
4609
|
try {
|
|
4363
|
-
return parseProjectAlias(JSON.parse(await
|
|
4610
|
+
return parseProjectAlias(JSON.parse(await readFile6(path, "utf8")));
|
|
4364
4611
|
} catch (error) {
|
|
4365
4612
|
if (isNotFoundError(error) || error instanceof ProjectRegistrationError)
|
|
4366
4613
|
return null;
|
|
@@ -4439,7 +4686,7 @@ async function listLegacyHookBindings(homeDir) {
|
|
|
4439
4686
|
for (const root of roots) {
|
|
4440
4687
|
for (const sessionDir of await listDirectoryNames3(root)) {
|
|
4441
4688
|
try {
|
|
4442
|
-
const value = JSON.parse(await
|
|
4689
|
+
const value = JSON.parse(await readFile6(join8(root, sessionDir, "binding.json"), "utf8"));
|
|
4443
4690
|
if (value === null || value.target !== "claude" && value.target !== "codex" || !isNonEmptyString(value.sessionKey) || !(value.cwd === null || isAbsoluteString(value.cwd)) || !isNonEmptyString(value.updatedAt)) {
|
|
4444
4691
|
continue;
|
|
4445
4692
|
}
|
|
@@ -4560,6 +4807,7 @@ async function createSegment(input) {
|
|
|
4560
4807
|
String(toLine),
|
|
4561
4808
|
input.signals.at(-1)?.eventId ?? input.now
|
|
4562
4809
|
]);
|
|
4810
|
+
const rawSha256 = sha256Hex(truncated.content);
|
|
4563
4811
|
return {
|
|
4564
4812
|
schemaVersion: 1,
|
|
4565
4813
|
kind: "session-evidence-segment",
|
|
@@ -4572,6 +4820,13 @@ async function createSegment(input) {
|
|
|
4572
4820
|
createdAt: input.now,
|
|
4573
4821
|
reason: input.reason,
|
|
4574
4822
|
strength: input.strength,
|
|
4823
|
+
retention: {
|
|
4824
|
+
policyDays: input.state.policy.retentionDays,
|
|
4825
|
+
expiresAt: addDays2(input.now, input.state.policy.retentionDays),
|
|
4826
|
+
rawState: "available",
|
|
4827
|
+
rawPurgedAt: null,
|
|
4828
|
+
originalRawSha256: rawSha256
|
|
4829
|
+
},
|
|
4575
4830
|
source: {
|
|
4576
4831
|
traceRefId: input.cursor.traceRefId,
|
|
4577
4832
|
sourcePath: input.paths.eventsPath,
|
|
@@ -4589,7 +4844,7 @@ async function createSegment(input) {
|
|
|
4589
4844
|
content: truncated.content,
|
|
4590
4845
|
truncated: raw.truncated || truncated.truncated,
|
|
4591
4846
|
byteLength: Buffer.byteLength(truncated.content, "utf8"),
|
|
4592
|
-
sha256:
|
|
4847
|
+
sha256: rawSha256
|
|
4593
4848
|
},
|
|
4594
4849
|
normalized: {
|
|
4595
4850
|
summary: createSegmentSummary(input.reason, input.strength, signalSummaries),
|
|
@@ -4618,6 +4873,12 @@ async function createSegment(input) {
|
|
|
4618
4873
|
}
|
|
4619
4874
|
};
|
|
4620
4875
|
}
|
|
4876
|
+
function addDays2(value, days) {
|
|
4877
|
+
const timestamp = Date.parse(value);
|
|
4878
|
+
if (!Number.isFinite(timestamp))
|
|
4879
|
+
throw new Error("Invalid Session Evidence retention timestamp.");
|
|
4880
|
+
return new Date(timestamp + days * 24 * 60 * 60 * 1000).toISOString();
|
|
4881
|
+
}
|
|
4621
4882
|
function estimateTokenCount(rawPayloadJson, summary) {
|
|
4622
4883
|
return Math.max(1, Math.ceil((rawPayloadJson.length + summary.length) / 4));
|
|
4623
4884
|
}
|
|
@@ -4832,8 +5093,8 @@ async function updateSessionMemoryFromHook(input) {
|
|
|
4832
5093
|
const cwd = optionalString(input.rawPayload.cwd);
|
|
4833
5094
|
const workspace = team === null && cwd !== null ? await resolveProjectWorkspaceFromCwd({ homeDir: input.homeDir, cwd }) : null;
|
|
4834
5095
|
const projectKey = sanitizeStorageId(team?.projectKey ?? workspace?.projectKey ?? resolveProjectLogKey(input.homeDir, cwd ?? input.homeDir), "project");
|
|
4835
|
-
const
|
|
4836
|
-
const paths = resolveSessionMemoryPaths({ homeDir: input.homeDir, projectKey, sessionKey });
|
|
5096
|
+
const sessionKey2 = sanitizeStorageId(resolveTraceSessionKey(input.rawPayload), "session");
|
|
5097
|
+
const paths = resolveSessionMemoryPaths({ homeDir: input.homeDir, projectKey, sessionKey: sessionKey2 });
|
|
4837
5098
|
const now = normalizeTimestamp(input.receivedAt ?? input.event.time.receivedAt);
|
|
4838
5099
|
const existingState = await readSessionState(paths.statePath);
|
|
4839
5100
|
if (existingState === null && input.event.type !== "UserPromptSubmit") {
|
|
@@ -4845,7 +5106,7 @@ async function updateSessionMemoryFromHook(input) {
|
|
|
4845
5106
|
const currentSignal = createSignal(input, rawEvent, appended.lineNumber);
|
|
4846
5107
|
const state = existingState ?? createInitialState({
|
|
4847
5108
|
projectKey,
|
|
4848
|
-
sessionKey,
|
|
5109
|
+
sessionKey: sessionKey2,
|
|
4849
5110
|
target: input.target,
|
|
4850
5111
|
runId: team?.runId ?? null,
|
|
4851
5112
|
roleId: team?.roleId ?? null,
|
|
@@ -4854,7 +5115,7 @@ async function updateSessionMemoryFromHook(input) {
|
|
|
4854
5115
|
eventId: input.event.eventId,
|
|
4855
5116
|
tokenEstimate
|
|
4856
5117
|
});
|
|
4857
|
-
const cursor = await readSessionCursor(paths.cursorPath,
|
|
5118
|
+
const cursor = await readSessionCursor(paths.cursorPath, sessionKey2, paths.eventsPath, now);
|
|
4858
5119
|
const previous = cloneState(state);
|
|
4859
5120
|
applyEventToState(state, {
|
|
4860
5121
|
input,
|
|
@@ -4896,21 +5157,24 @@ async function updateSessionMemoryFromHook(input) {
|
|
|
4896
5157
|
segmentPath = paths.segmentPath(segment.id);
|
|
4897
5158
|
await writeJson(segmentPath, segment);
|
|
4898
5159
|
await writeSessionIndex(paths, state, segment, now);
|
|
4899
|
-
|
|
4900
|
-
|
|
4901
|
-
|
|
4902
|
-
|
|
4903
|
-
|
|
4904
|
-
|
|
4905
|
-
|
|
4906
|
-
|
|
4907
|
-
|
|
4908
|
-
|
|
4909
|
-
|
|
4910
|
-
|
|
4911
|
-
|
|
4912
|
-
|
|
4913
|
-
|
|
5160
|
+
if (shouldQueueSegmentForEvolution(segment)) {
|
|
5161
|
+
const trigger = await enqueueSegmentEvolutionTrigger({
|
|
5162
|
+
homeDir: input.homeDir,
|
|
5163
|
+
projectKey: segment.projectKey,
|
|
5164
|
+
sessionKey: segment.sessionKey,
|
|
5165
|
+
runId: segment.runId,
|
|
5166
|
+
roleId: segment.roleId,
|
|
5167
|
+
segmentId: segment.id,
|
|
5168
|
+
segmentPath,
|
|
5169
|
+
strength: segment.strength,
|
|
5170
|
+
reason: segment.reason,
|
|
5171
|
+
summary: segment.normalized.summary,
|
|
5172
|
+
now
|
|
5173
|
+
});
|
|
5174
|
+
queuedSegmentTriggerId = trigger.id;
|
|
5175
|
+
}
|
|
5176
|
+
await resetSessionRawEvents(paths.eventsPath);
|
|
5177
|
+
cursor.lastCapturedLine = 0;
|
|
4914
5178
|
cursor.lastCapturedEventId = input.event.eventId;
|
|
4915
5179
|
cursor.updatedAt = now;
|
|
4916
5180
|
state.lastSegmentId = segment.id;
|
|
@@ -4936,6 +5200,9 @@ async function updateSessionMemoryFromHook(input) {
|
|
|
4936
5200
|
queuedSegmentTriggerId
|
|
4937
5201
|
};
|
|
4938
5202
|
}
|
|
5203
|
+
function shouldQueueSegmentForEvolution(segment) {
|
|
5204
|
+
return segment.reason !== "session-memory-threshold" && segment.reason !== "session-memory-init";
|
|
5205
|
+
}
|
|
4939
5206
|
function emptyResult() {
|
|
4940
5207
|
return {
|
|
4941
5208
|
state: null,
|
|
@@ -4947,12 +5214,12 @@ function emptyResult() {
|
|
|
4947
5214
|
};
|
|
4948
5215
|
}
|
|
4949
5216
|
// packages/core/src/hooks/index.ts
|
|
4950
|
-
import { mkdir as mkdir6, readFile as
|
|
5217
|
+
import { mkdir as mkdir6, readFile as readFile8, writeFile as writeFile5 } from "node:fs/promises";
|
|
4951
5218
|
import { dirname as dirname7, join as join11 } from "node:path";
|
|
4952
5219
|
|
|
4953
5220
|
// packages/core/src/team/index.ts
|
|
4954
5221
|
import { spawn } from "node:child_process";
|
|
4955
|
-
import { appendFile as appendFile3, cp, mkdir as mkdir5, readFile as
|
|
5222
|
+
import { appendFile as appendFile3, cp, mkdir as mkdir5, readFile as readFile7, readdir as readdir6, stat as stat3, writeFile as writeFile4 } from "node:fs/promises";
|
|
4956
5223
|
import { basename as basename3, dirname as dirname6, extname, isAbsolute as isAbsolute4, join as join10, relative as relative3, resolve as resolve3 } from "node:path";
|
|
4957
5224
|
|
|
4958
5225
|
// packages/core/src/team/prompts.ts
|
|
@@ -5163,7 +5430,7 @@ function createTeamRunStore(homeDir) {
|
|
|
5163
5430
|
},
|
|
5164
5431
|
async readRun(runId) {
|
|
5165
5432
|
await migrateLegacyRunDirIfNeeded(paths2, runId);
|
|
5166
|
-
return parseTeamRunRecord(JSON.parse(await
|
|
5433
|
+
return parseTeamRunRecord(JSON.parse(await readFile7(paths2.runPath(runId), "utf8")));
|
|
5167
5434
|
},
|
|
5168
5435
|
async writeLatestRunId(runId) {
|
|
5169
5436
|
await mkdir5(paths2.runsDir, { recursive: true });
|
|
@@ -5171,13 +5438,13 @@ function createTeamRunStore(homeDir) {
|
|
|
5171
5438
|
},
|
|
5172
5439
|
async readLatestRunId() {
|
|
5173
5440
|
try {
|
|
5174
|
-
const latest = JSON.parse(await
|
|
5441
|
+
const latest = JSON.parse(await readFile7(paths2.latestRunPath, "utf8"));
|
|
5175
5442
|
if (latest?.version === 1 && typeof latest.runId === "string")
|
|
5176
5443
|
return latest.runId;
|
|
5177
5444
|
return null;
|
|
5178
5445
|
} catch {
|
|
5179
5446
|
try {
|
|
5180
|
-
const latest = JSON.parse(await
|
|
5447
|
+
const latest = JSON.parse(await readFile7(paths2.legacyLatestRunPath, "utf8"));
|
|
5181
5448
|
if (latest?.version === 1 && typeof latest.runId === "string") {
|
|
5182
5449
|
await migrateLegacyRunDirIfNeeded(paths2, latest.runId);
|
|
5183
5450
|
await this.writeLatestRunId(latest.runId);
|
|
@@ -5195,13 +5462,13 @@ function createTeamRunStore(homeDir) {
|
|
|
5195
5462
|
},
|
|
5196
5463
|
async readAgent(runId, roleId) {
|
|
5197
5464
|
await migrateLegacyRunDirIfNeeded(paths2, runId);
|
|
5198
|
-
return parseTeamAgentRecord(JSON.parse(await
|
|
5465
|
+
return parseTeamAgentRecord(JSON.parse(await readFile7(paths2.agentPath(runId, roleId), "utf8")));
|
|
5199
5466
|
},
|
|
5200
5467
|
async readAgents(runId) {
|
|
5201
5468
|
await migrateLegacyRunDirIfNeeded(paths2, runId);
|
|
5202
5469
|
try {
|
|
5203
5470
|
const entries = await readdir6(paths2.agentsDir(runId));
|
|
5204
|
-
const agents = await Promise.all(entries.filter((entry) => entry.endsWith(".json")).map((entry) =>
|
|
5471
|
+
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)))));
|
|
5205
5472
|
return agents.sort((left, right) => left.roleId.localeCompare(right.roleId));
|
|
5206
5473
|
} catch {
|
|
5207
5474
|
return [];
|
|
@@ -5218,7 +5485,7 @@ function createTeamRunStore(homeDir) {
|
|
|
5218
5485
|
async readMessageList(runId) {
|
|
5219
5486
|
await migrateLegacyRunDirIfNeeded(paths2, runId);
|
|
5220
5487
|
try {
|
|
5221
|
-
const file = parseTeamMessageListFile(JSON.parse(await
|
|
5488
|
+
const file = parseTeamMessageListFile(JSON.parse(await readFile7(paths2.messageListPath(runId), "utf8")));
|
|
5222
5489
|
return file.messages;
|
|
5223
5490
|
} catch (error) {
|
|
5224
5491
|
if (isNotFoundError2(error))
|
|
@@ -6157,7 +6424,7 @@ async function resolveTeamOverlay(input) {
|
|
|
6157
6424
|
return {
|
|
6158
6425
|
source: "repo",
|
|
6159
6426
|
teamPath: repoTeamPath,
|
|
6160
|
-
definition: parseTeamDefinitionMarkdown(await
|
|
6427
|
+
definition: parseTeamDefinitionMarkdown(await readFile7(repoTeamPath, "utf8"))
|
|
6161
6428
|
};
|
|
6162
6429
|
}
|
|
6163
6430
|
const globalTeamPath = resolveGlobalTeamMarkdownPath(input.homeDir);
|
|
@@ -6165,7 +6432,7 @@ async function resolveTeamOverlay(input) {
|
|
|
6165
6432
|
return {
|
|
6166
6433
|
source: "global",
|
|
6167
6434
|
teamPath: globalTeamPath,
|
|
6168
|
-
definition: parseTeamDefinitionMarkdown(await
|
|
6435
|
+
definition: parseTeamDefinitionMarkdown(await readFile7(globalTeamPath, "utf8"))
|
|
6169
6436
|
};
|
|
6170
6437
|
}
|
|
6171
6438
|
return {
|
|
@@ -6180,7 +6447,7 @@ async function ensureDefaultTeamOverlay(input) {
|
|
|
6180
6447
|
const files = [];
|
|
6181
6448
|
for (const asset of assets) {
|
|
6182
6449
|
const targetPath = asset.kind === "team" ? join10(paths2.rootDir, "team", "team.md") : join10(paths2.rootDir, "team", "agents", asset.name);
|
|
6183
|
-
const content = await
|
|
6450
|
+
const content = await readFile7(asset.sourcePath, "utf8");
|
|
6184
6451
|
files.push({
|
|
6185
6452
|
sourcePath: asset.sourcePath,
|
|
6186
6453
|
targetPath,
|
|
@@ -6727,13 +6994,13 @@ async function createScopedTeamStartupContext(input) {
|
|
|
6727
6994
|
});
|
|
6728
6995
|
if (pack === null)
|
|
6729
6996
|
return null;
|
|
6730
|
-
const
|
|
6731
|
-
if (await hasContextInjectionReceipt({ homeDir, sessionKey, contextPackId: pack.id })) {
|
|
6997
|
+
const sessionKey2 = `team-${input.runId}-${input.role.roleId}`;
|
|
6998
|
+
if (await hasContextInjectionReceipt({ homeDir, sessionKey: sessionKey2, contextPackId: pack.id })) {
|
|
6732
6999
|
return null;
|
|
6733
7000
|
}
|
|
6734
7001
|
await writeContextInjectionReceipt({
|
|
6735
7002
|
homeDir,
|
|
6736
|
-
sessionKey,
|
|
7003
|
+
sessionKey: sessionKey2,
|
|
6737
7004
|
pack,
|
|
6738
7005
|
trigger: "team-startup",
|
|
6739
7006
|
hookEventId: null,
|
|
@@ -6852,7 +7119,7 @@ function recoveredHandle(input, window, paneId) {
|
|
|
6852
7119
|
async function readSettingsOrDefault(homeDir) {
|
|
6853
7120
|
const settingsPath = resolveEvoDevPaths(homeDir).settingsPath;
|
|
6854
7121
|
try {
|
|
6855
|
-
return parseSettings(JSON.parse(await
|
|
7122
|
+
return parseSettings(JSON.parse(await readFile7(settingsPath, "utf8")));
|
|
6856
7123
|
} catch {
|
|
6857
7124
|
return createDefaultSettings();
|
|
6858
7125
|
}
|
|
@@ -6930,7 +7197,7 @@ async function assertReadableFile(path, label) {
|
|
|
6930
7197
|
}
|
|
6931
7198
|
async function writeTextFileIfMissing(path, content) {
|
|
6932
7199
|
try {
|
|
6933
|
-
await
|
|
7200
|
+
await readFile7(path, "utf8");
|
|
6934
7201
|
return false;
|
|
6935
7202
|
} catch (error) {
|
|
6936
7203
|
if (!isNotFoundError2(error)) {
|
|
@@ -6963,7 +7230,7 @@ async function readTeamAgentMarkdown(reference) {
|
|
|
6963
7230
|
throw new Error(`Team agent file for ${reference.roleId} must be Markdown.`);
|
|
6964
7231
|
}
|
|
6965
7232
|
try {
|
|
6966
|
-
return await
|
|
7233
|
+
return await readFile7(reference.sourcePath, "utf8");
|
|
6967
7234
|
} catch (error) {
|
|
6968
7235
|
if (isNotFoundError2(error)) {
|
|
6969
7236
|
throw new Error(`Team agent file not found for ${reference.roleId}: ${reference.sourcePath}`);
|
|
@@ -7299,7 +7566,7 @@ function defaultRolePrompt(roleId) {
|
|
|
7299
7566
|
}
|
|
7300
7567
|
async function readRoleCandidate(path, source) {
|
|
7301
7568
|
try {
|
|
7302
|
-
const raw = await
|
|
7569
|
+
const raw = await readFile7(path, "utf8");
|
|
7303
7570
|
return { value: JSON.parse(raw), path, source };
|
|
7304
7571
|
} catch (error) {
|
|
7305
7572
|
if (isNotFoundError2(error))
|
|
@@ -7335,7 +7602,7 @@ function resolveTeamBindingWritePath(input) {
|
|
|
7335
7602
|
}
|
|
7336
7603
|
async function readTeamBindingConfig(path) {
|
|
7337
7604
|
try {
|
|
7338
|
-
const raw = await
|
|
7605
|
+
const raw = await readFile7(path, "utf8");
|
|
7339
7606
|
return parseTeamBindingConfig(JSON.parse(raw));
|
|
7340
7607
|
} catch (error) {
|
|
7341
7608
|
if (isNotFoundError2(error)) {
|
|
@@ -7412,7 +7679,7 @@ async function appendJsonLine2(path, value) {
|
|
|
7412
7679
|
}
|
|
7413
7680
|
async function readJsonLines(path, parse2) {
|
|
7414
7681
|
try {
|
|
7415
|
-
const text2 = await
|
|
7682
|
+
const text2 = await readFile7(path, "utf8");
|
|
7416
7683
|
return text2.split(`
|
|
7417
7684
|
`).filter((line) => line.trim() !== "").map((line) => parse2(JSON.parse(line)));
|
|
7418
7685
|
} catch (error) {
|
|
@@ -7739,8 +8006,20 @@ async function handleHookRuntime(input) {
|
|
|
7739
8006
|
const evolutionDiagnostics = sessionMemoryDiagnostics.queuedSegmentTriggerId !== null || shouldSuppressLegacyEvolutionTrigger(input.event.type) ? { stateWrites: [], warnings: [] } : await recordEvolutionTriggerFromHook(input);
|
|
7740
8007
|
const messageDiagnostics = await deliverPendingTeamMessagesFromHook(input, result);
|
|
7741
8008
|
const scopedContextDiagnostics = await injectScopedKnowledgeContextFromHook(input, result);
|
|
8009
|
+
const knowledgeOutcomeDiagnostics = await recordKnowledgeOutcomeFromHook(input);
|
|
7742
8010
|
const teamStateDiagnostics = await recordTeamAgentHookStateFromHook(input);
|
|
7743
|
-
|
|
8011
|
+
let completed = appendRuntimeDiagnostics(result, diagnostics);
|
|
8012
|
+
for (const entry of [
|
|
8013
|
+
projectDiagnostics,
|
|
8014
|
+
sessionMemoryDiagnostics,
|
|
8015
|
+
evolutionDiagnostics,
|
|
8016
|
+
messageDiagnostics,
|
|
8017
|
+
scopedContextDiagnostics,
|
|
8018
|
+
knowledgeOutcomeDiagnostics,
|
|
8019
|
+
teamStateDiagnostics
|
|
8020
|
+
]) {
|
|
8021
|
+
completed = appendRuntimeDiagnostics(completed, entry);
|
|
8022
|
+
}
|
|
7744
8023
|
return appendDevelopmentHookDiagnostics(input, completed);
|
|
7745
8024
|
}
|
|
7746
8025
|
function formatHookRuntimeOutput(result) {
|
|
@@ -7927,7 +8206,9 @@ async function injectScopedKnowledgeContextFromHook(input, result) {
|
|
|
7927
8206
|
environment: input.environment,
|
|
7928
8207
|
payload: input.rawPayload
|
|
7929
8208
|
});
|
|
7930
|
-
if (team === null
|
|
8209
|
+
if (team === null)
|
|
8210
|
+
return injectOrdinarySessionKnowledge(input, result);
|
|
8211
|
+
if (team.roleId !== "main")
|
|
7931
8212
|
return { stateWrites: [], warnings: [] };
|
|
7932
8213
|
try {
|
|
7933
8214
|
if (input.runtimeInjectionEnabled === false)
|
|
@@ -7939,17 +8220,17 @@ async function injectScopedKnowledgeContextFromHook(input, result) {
|
|
|
7939
8220
|
});
|
|
7940
8221
|
if (pack === null)
|
|
7941
8222
|
return { stateWrites: [], warnings: [] };
|
|
7942
|
-
const
|
|
8223
|
+
const sessionKey2 = resolveHookSessionKey(input.rawPayload);
|
|
7943
8224
|
if (await hasContextInjectionReceipt({
|
|
7944
8225
|
homeDir: input.homeDir,
|
|
7945
|
-
sessionKey,
|
|
8226
|
+
sessionKey: sessionKey2,
|
|
7946
8227
|
contextPackId: pack.id
|
|
7947
8228
|
})) {
|
|
7948
8229
|
return { stateWrites: [], warnings: [] };
|
|
7949
8230
|
}
|
|
7950
8231
|
const receipt = await writeContextInjectionReceipt({
|
|
7951
8232
|
homeDir: input.homeDir,
|
|
7952
|
-
sessionKey,
|
|
8233
|
+
sessionKey: sessionKey2,
|
|
7953
8234
|
pack,
|
|
7954
8235
|
trigger: "hook-safe-point",
|
|
7955
8236
|
hookEventId: input.event.eventId,
|
|
@@ -7964,6 +8245,102 @@ async function injectScopedKnowledgeContextFromHook(input, result) {
|
|
|
7964
8245
|
};
|
|
7965
8246
|
}
|
|
7966
8247
|
}
|
|
8248
|
+
async function injectOrdinarySessionKnowledge(input, result) {
|
|
8249
|
+
if (input.event.type !== "UserPromptSubmit" || input.runtimeInjectionEnabled === false) {
|
|
8250
|
+
return { stateWrites: [], warnings: [] };
|
|
8251
|
+
}
|
|
8252
|
+
const cwd = optionalPayloadString(input.rawPayload.cwd);
|
|
8253
|
+
if (cwd === null)
|
|
8254
|
+
return { stateWrites: [], warnings: [] };
|
|
8255
|
+
const sessionKey2 = resolveHookSessionKey(input.rawPayload);
|
|
8256
|
+
const paths2 = resolveHookRuntimeSessionPaths({ homeDir: input.homeDir, sessionKey: sessionKey2 });
|
|
8257
|
+
const binding = await readSessionBinding(input.homeDir, input.rawPayload);
|
|
8258
|
+
if (binding?.knowledgeContextDeliveredAt !== undefined && binding.knowledgeContextDeliveredAt !== null) {
|
|
8259
|
+
return { stateWrites: [], warnings: [] };
|
|
8260
|
+
}
|
|
8261
|
+
try {
|
|
8262
|
+
const workspace = await resolveProjectWorkspaceFromCwd({ homeDir: input.homeDir, cwd });
|
|
8263
|
+
if (workspace === null)
|
|
8264
|
+
return { stateWrites: [], warnings: [] };
|
|
8265
|
+
const queryText = readKnowledgeQueryText(input.rawPayload);
|
|
8266
|
+
const pack = await createScopedKnowledgeContextPack({
|
|
8267
|
+
homeDir: input.homeDir,
|
|
8268
|
+
projectKey: workspace.projectKey,
|
|
8269
|
+
roleId: input.target,
|
|
8270
|
+
...queryText === null ? {} : { queryText },
|
|
8271
|
+
limit: 3,
|
|
8272
|
+
inlineOnly: true
|
|
8273
|
+
});
|
|
8274
|
+
const deliveredAt = input.receivedAt ?? new Date().toISOString();
|
|
8275
|
+
const nextBinding = {
|
|
8276
|
+
version: 1,
|
|
8277
|
+
target: input.target,
|
|
8278
|
+
sessionKey: sessionKey2,
|
|
8279
|
+
cwd,
|
|
8280
|
+
teamRuntimeContextDeliveredAt: binding?.teamRuntimeContextDeliveredAt ?? null,
|
|
8281
|
+
knowledgeContextDeliveredAt: deliveredAt,
|
|
8282
|
+
updatedAt: deliveredAt
|
|
8283
|
+
};
|
|
8284
|
+
await writeJsonFile2(paths2.bindingPath, nextBinding);
|
|
8285
|
+
if (pack === null)
|
|
8286
|
+
return { stateWrites: [paths2.bindingPath], warnings: [] };
|
|
8287
|
+
const receipt = await writeContextInjectionReceipt({
|
|
8288
|
+
homeDir: input.homeDir,
|
|
8289
|
+
sessionKey: sessionKey2,
|
|
8290
|
+
pack,
|
|
8291
|
+
trigger: "ordinary-session",
|
|
8292
|
+
hookEventId: input.event.eventId,
|
|
8293
|
+
injectedAt: deliveredAt
|
|
8294
|
+
});
|
|
8295
|
+
result.output = appendAdditionalContext(result.output, input.event.type, formatScopedKnowledgePromptBlock(pack));
|
|
8296
|
+
return { stateWrites: [paths2.bindingPath, receipt.path], warnings: [] };
|
|
8297
|
+
} catch {
|
|
8298
|
+
return {
|
|
8299
|
+
stateWrites: [],
|
|
8300
|
+
warnings: ["Ordinary-session knowledge context could not be injected."]
|
|
8301
|
+
};
|
|
8302
|
+
}
|
|
8303
|
+
}
|
|
8304
|
+
function readKnowledgeQueryText(payload) {
|
|
8305
|
+
for (const value of [
|
|
8306
|
+
payload.prompt,
|
|
8307
|
+
payload.user_prompt,
|
|
8308
|
+
payload.userPrompt,
|
|
8309
|
+
payload.query,
|
|
8310
|
+
payload.message,
|
|
8311
|
+
payload.text
|
|
8312
|
+
]) {
|
|
8313
|
+
if (typeof value === "string" && value.trim() !== "")
|
|
8314
|
+
return value.trim().slice(0, 4000);
|
|
8315
|
+
}
|
|
8316
|
+
return null;
|
|
8317
|
+
}
|
|
8318
|
+
async function recordKnowledgeOutcomeFromHook(input) {
|
|
8319
|
+
if (!isKnowledgeOutcomeSignal(input.event))
|
|
8320
|
+
return { stateWrites: [], warnings: [] };
|
|
8321
|
+
try {
|
|
8322
|
+
const paths2 = await recordContextInjectionOutcome({
|
|
8323
|
+
homeDir: input.homeDir,
|
|
8324
|
+
sessionKey: resolveHookSessionKey(input.rawPayload),
|
|
8325
|
+
eventId: input.event.eventId,
|
|
8326
|
+
observedAt: input.receivedAt ?? input.event.time.receivedAt ?? new Date().toISOString(),
|
|
8327
|
+
summary: input.event.payload.summary
|
|
8328
|
+
});
|
|
8329
|
+
return { stateWrites: paths2, warnings: [] };
|
|
8330
|
+
} catch {
|
|
8331
|
+
return {
|
|
8332
|
+
stateWrites: [],
|
|
8333
|
+
warnings: ["Knowledge delivery outcome metadata could not be recorded."]
|
|
8334
|
+
};
|
|
8335
|
+
}
|
|
8336
|
+
}
|
|
8337
|
+
function isKnowledgeOutcomeSignal(event) {
|
|
8338
|
+
if (event.type !== "PostToolUse")
|
|
8339
|
+
return false;
|
|
8340
|
+
if (event.payload.metadata.commandClass === "test-command")
|
|
8341
|
+
return true;
|
|
8342
|
+
return /\b(?:test|lint|typecheck|build|verify|check)\b/iu.test(event.payload.summary);
|
|
8343
|
+
}
|
|
7967
8344
|
function canDeliverTeamMessagesFromHook(target, eventType) {
|
|
7968
8345
|
if (!TEAM_MESSAGE_DELIVERY_EVENTS.has(eventType))
|
|
7969
8346
|
return false;
|
|
@@ -8011,17 +8388,18 @@ async function handleSessionStart(input) {
|
|
|
8011
8388
|
});
|
|
8012
8389
|
}
|
|
8013
8390
|
async function handleUserPromptSubmit(input) {
|
|
8014
|
-
const
|
|
8015
|
-
const paths2 = resolveHookRuntimeSessionPaths({ homeDir: input.homeDir, sessionKey });
|
|
8391
|
+
const sessionKey2 = resolveHookSessionKey(input.rawPayload);
|
|
8392
|
+
const paths2 = resolveHookRuntimeSessionPaths({ homeDir: input.homeDir, sessionKey: sessionKey2 });
|
|
8016
8393
|
const previousBinding = await readSessionBinding(input.homeDir, input.rawPayload);
|
|
8017
8394
|
const teamRuntimeContext = previousBinding?.teamRuntimeContextDeliveredAt === undefined || previousBinding.teamRuntimeContextDeliveredAt === null ? await createTeamRuntimeContextForUserPrompt(input) : null;
|
|
8018
8395
|
const teamRuntimeContextDeliveredAt = teamRuntimeContext !== null ? input.receivedAt ?? new Date().toISOString() : previousBinding?.teamRuntimeContextDeliveredAt ?? null;
|
|
8019
8396
|
const binding = {
|
|
8020
8397
|
version: 1,
|
|
8021
8398
|
target: input.target,
|
|
8022
|
-
sessionKey,
|
|
8399
|
+
sessionKey: sessionKey2,
|
|
8023
8400
|
cwd: optionalPayloadString(input.rawPayload.cwd),
|
|
8024
8401
|
teamRuntimeContextDeliveredAt,
|
|
8402
|
+
knowledgeContextDeliveredAt: previousBinding?.knowledgeContextDeliveredAt ?? null,
|
|
8025
8403
|
updatedAt: input.receivedAt ?? new Date().toISOString()
|
|
8026
8404
|
};
|
|
8027
8405
|
await writeJsonFile2(paths2.bindingPath, binding);
|
|
@@ -8179,10 +8557,10 @@ function truncateTeamMessageBody(value) {
|
|
|
8179
8557
|
return `${value.slice(0, 4000)}...[truncated:${value.length - 4000}]`;
|
|
8180
8558
|
}
|
|
8181
8559
|
async function readSessionBinding(homeDir, payload) {
|
|
8182
|
-
const
|
|
8183
|
-
const paths2 = resolveHookRuntimeSessionPaths({ homeDir, sessionKey });
|
|
8560
|
+
const sessionKey2 = resolveHookSessionKey(payload);
|
|
8561
|
+
const paths2 = resolveHookRuntimeSessionPaths({ homeDir, sessionKey: sessionKey2 });
|
|
8184
8562
|
try {
|
|
8185
|
-
return JSON.parse(await
|
|
8563
|
+
return JSON.parse(await readFile8(paths2.bindingPath, "utf8"));
|
|
8186
8564
|
} catch (error) {
|
|
8187
8565
|
if (isNotFoundError3(error))
|
|
8188
8566
|
return null;
|
|
@@ -8457,7 +8835,7 @@ function mergeSettings(existing, defaults = createDefaultSettings()) {
|
|
|
8457
8835
|
async function readRuntimeInjectionSettings(homeDir) {
|
|
8458
8836
|
const paths2 = resolveEvoDevPaths(homeDir);
|
|
8459
8837
|
try {
|
|
8460
|
-
return parseSettings(JSON.parse(await
|
|
8838
|
+
return parseSettings(JSON.parse(await readFile9(paths2.settingsPath, "utf8"))).memory;
|
|
8461
8839
|
} catch (error) {
|
|
8462
8840
|
if (isNotFoundError4(error))
|
|
8463
8841
|
return createDefaultMemorySettings();
|
|
@@ -8610,7 +8988,7 @@ import { createHash as createHash3, randomUUID as randomUUID2 } from "node:crypt
|
|
|
8610
8988
|
import {
|
|
8611
8989
|
appendFile as appendFile4,
|
|
8612
8990
|
mkdir as mkdir7,
|
|
8613
|
-
readFile as
|
|
8991
|
+
readFile as readFile10,
|
|
8614
8992
|
readdir as readdir7,
|
|
8615
8993
|
rename as rename2,
|
|
8616
8994
|
rm as rm3,
|
|
@@ -8708,7 +9086,7 @@ async function listOkfKnowledgeChangeCandidates(input) {
|
|
|
8708
9086
|
for (const projectKey of projectKeys) {
|
|
8709
9087
|
const paths2 = resolveKnowledgeChangePaths(input.homeDir, projectKey);
|
|
8710
9088
|
for (const name of await listJsonFileNames(paths2.candidatesDir)) {
|
|
8711
|
-
const value = JSON.parse(await
|
|
9089
|
+
const value = JSON.parse(await readFile10(join12(paths2.candidatesDir, name), "utf8"));
|
|
8712
9090
|
const change = parseOkfKnowledgeChangeCandidate(value);
|
|
8713
9091
|
if (input.state === undefined || change.state === input.state)
|
|
8714
9092
|
changes.push(change);
|
|
@@ -8724,7 +9102,7 @@ async function readOkfKnowledgeChangeCandidate(input) {
|
|
|
8724
9102
|
if (input.projectKey !== undefined) {
|
|
8725
9103
|
const paths3 = resolveKnowledgeChangePaths(input.homeDir, input.projectKey);
|
|
8726
9104
|
const path = join12(paths3.candidatesDir, `${changeId}.json`);
|
|
8727
|
-
const change2 = parseOkfKnowledgeChangeCandidate(JSON.parse(await
|
|
9105
|
+
const change2 = parseOkfKnowledgeChangeCandidate(JSON.parse(await readFile10(path, "utf8")));
|
|
8728
9106
|
return { path, change: change2 };
|
|
8729
9107
|
}
|
|
8730
9108
|
const matches = await listOkfKnowledgeChangeCandidates({ homeDir: input.homeDir });
|
|
@@ -8745,7 +9123,7 @@ async function replaceOkfKnowledgeChangeCandidate(input) {
|
|
|
8745
9123
|
}
|
|
8746
9124
|
const paths2 = resolveKnowledgeChangePaths(input.homeDir, input.next.projectKey);
|
|
8747
9125
|
const path = join12(paths2.candidatesDir, `${input.next.id}.json`);
|
|
8748
|
-
const current = parseOkfKnowledgeChangeCandidate(JSON.parse(await
|
|
9126
|
+
const current = parseOkfKnowledgeChangeCandidate(JSON.parse(await readFile10(path, "utf8")));
|
|
8749
9127
|
if (current.state !== input.previous.state || current.updatedAt !== input.previous.updatedAt || current.candidate.revision !== input.previous.candidate.revision) {
|
|
8750
9128
|
throw new Error("Knowledge change was modified by another decision.");
|
|
8751
9129
|
}
|
|
@@ -8854,7 +9232,7 @@ function resolveKnowledgeChangePaths(homeDir, projectKey) {
|
|
|
8854
9232
|
}
|
|
8855
9233
|
async function readKnowledgeChangeLockToken(path) {
|
|
8856
9234
|
try {
|
|
8857
|
-
const value = JSON.parse(await
|
|
9235
|
+
const value = JSON.parse(await readFile10(path, "utf8"));
|
|
8858
9236
|
return isRecord6(value) && typeof value.token === "string" ? value.token : null;
|
|
8859
9237
|
} catch {
|
|
8860
9238
|
return null;
|
|
@@ -8885,7 +9263,7 @@ async function recoverStaleKnowledgeChangeLock(path) {
|
|
|
8885
9263
|
}
|
|
8886
9264
|
async function readKnowledgeChangeLockOwner(path) {
|
|
8887
9265
|
try {
|
|
8888
|
-
const value = JSON.parse(await
|
|
9266
|
+
const value = JSON.parse(await readFile10(path, "utf8"));
|
|
8889
9267
|
if (!isRecord6(value))
|
|
8890
9268
|
return null;
|
|
8891
9269
|
return {
|
|
@@ -9010,6 +9388,7 @@ function createOkfKnowledgeRuntimeProjectionFromCandidate(candidate) {
|
|
|
9010
9388
|
pathScopes: candidate.pathScopes
|
|
9011
9389
|
},
|
|
9012
9390
|
relatedConceptLinks: candidate.relatedConceptLinks,
|
|
9391
|
+
supportRef: candidate.supportRef ?? null,
|
|
9013
9392
|
lifecycle: {
|
|
9014
9393
|
status: candidate.decision === "revoke" ? "revoked" : "active",
|
|
9015
9394
|
supersedes: [],
|
|
@@ -9043,6 +9422,7 @@ function createOkfKnowledgeRuntimeProjectionFromConcept(concept) {
|
|
|
9043
9422
|
pathScopes: concept.pathScopes
|
|
9044
9423
|
},
|
|
9045
9424
|
relatedConceptLinks: readSectionList(sections, "Related Concepts").filter((item) => item !== "No related concepts yet."),
|
|
9425
|
+
supportRef: concept.supportRef ?? null,
|
|
9046
9426
|
lifecycle: {
|
|
9047
9427
|
status: concept.lifecycle.status,
|
|
9048
9428
|
supersedes: concept.lifecycle.supersedes,
|
|
@@ -9208,6 +9588,7 @@ function normalizeRuntimeProjection(value) {
|
|
|
9208
9588
|
pathScopes: normalizeArray(value.scopes.pathScopes)
|
|
9209
9589
|
},
|
|
9210
9590
|
relatedConceptLinks: normalizeArray(value.relatedConceptLinks),
|
|
9591
|
+
supportRef: value.supportRef,
|
|
9211
9592
|
lifecycle: {
|
|
9212
9593
|
status: value.lifecycle.status,
|
|
9213
9594
|
supersedes: normalizeArray(value.lifecycle.supersedes),
|
|
@@ -9235,6 +9616,7 @@ function listChangedProjectionFields(base, candidate) {
|
|
|
9235
9616
|
["scopes.workflowTags", base.scopes.workflowTags, candidate.scopes.workflowTags],
|
|
9236
9617
|
["scopes.pathScopes", base.scopes.pathScopes, candidate.scopes.pathScopes],
|
|
9237
9618
|
["relatedConceptLinks", base.relatedConceptLinks, candidate.relatedConceptLinks],
|
|
9619
|
+
["supportRef", base.supportRef, candidate.supportRef],
|
|
9238
9620
|
["lifecycle", base.lifecycle, candidate.lifecycle]
|
|
9239
9621
|
];
|
|
9240
9622
|
return fields.filter(([, left, right]) => stableJsonStringify(left) !== stableJsonStringify(right)).map(([field]) => field);
|
|
@@ -9301,36 +9683,103 @@ function stableJsonStringify(value) {
|
|
|
9301
9683
|
return `{${entries.map(([key, child]) => `${JSON.stringify(key)}:${stableJsonStringify(child)}`).join(",")}}`;
|
|
9302
9684
|
}
|
|
9303
9685
|
|
|
9304
|
-
// packages/core/src/evolution/knowledge/
|
|
9305
|
-
function
|
|
9686
|
+
// packages/core/src/evolution/knowledge/support.ts
|
|
9687
|
+
function resolveOkfKnowledgeRuntimeEligibility(input) {
|
|
9306
9688
|
const now = normalizeNow(input.now);
|
|
9307
|
-
|
|
9308
|
-
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)) {
|
|
9689
|
+
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)) {
|
|
9309
9690
|
return {
|
|
9691
|
+
delivery: "blocked",
|
|
9310
9692
|
freshness: "stale",
|
|
9311
|
-
reason: input.verifiedContradiction === true ? "Verified evidence contradicts the active claim." : "Lifecycle
|
|
9693
|
+
reason: input.verifiedContradiction === true ? "Verified evidence contradicts the active claim." : "Lifecycle state excludes this knowledge from runtime context."
|
|
9312
9694
|
};
|
|
9313
9695
|
}
|
|
9314
|
-
if (
|
|
9696
|
+
if (input.reviewState !== "accepted" && input.reviewState !== "auto-accepted") {
|
|
9315
9697
|
return {
|
|
9698
|
+
delivery: "blocked",
|
|
9316
9699
|
freshness: "unknown",
|
|
9317
|
-
reason: "
|
|
9700
|
+
reason: "Knowledge has not passed an active-compatible review state."
|
|
9318
9701
|
};
|
|
9319
9702
|
}
|
|
9320
|
-
if (input.
|
|
9703
|
+
if (input.sourceStatus === "changed") {
|
|
9321
9704
|
return {
|
|
9705
|
+
delivery: "blocked",
|
|
9322
9706
|
freshness: "review-due",
|
|
9323
|
-
reason:
|
|
9707
|
+
reason: "The supporting source changed and requires revalidation."
|
|
9708
|
+
};
|
|
9709
|
+
}
|
|
9710
|
+
if (isDue(input.lifecycle.reviewAfter, now)) {
|
|
9711
|
+
return {
|
|
9712
|
+
delivery: "reference",
|
|
9713
|
+
freshness: "review-due",
|
|
9714
|
+
reason: `Review was due at ${input.lifecycle.reviewAfter}.`
|
|
9715
|
+
};
|
|
9716
|
+
}
|
|
9717
|
+
if (input.supportRef === null) {
|
|
9718
|
+
return {
|
|
9719
|
+
delivery: "reference",
|
|
9720
|
+
freshness: "unknown",
|
|
9721
|
+
reason: "Legacy knowledge has no structured support reference."
|
|
9722
|
+
};
|
|
9723
|
+
}
|
|
9724
|
+
if (input.supportRef.kind === "semantic-inference") {
|
|
9725
|
+
if (input.reviewState === "accepted") {
|
|
9726
|
+
return {
|
|
9727
|
+
delivery: "inline",
|
|
9728
|
+
freshness: "verified",
|
|
9729
|
+
reason: "A human-accepted inference is runtime-eligible as an explicit decision."
|
|
9730
|
+
};
|
|
9731
|
+
}
|
|
9732
|
+
return {
|
|
9733
|
+
delivery: "reference",
|
|
9734
|
+
freshness: "unknown",
|
|
9735
|
+
reason: "Model-inferred knowledge remains on-demand until supported by a direct source."
|
|
9736
|
+
};
|
|
9737
|
+
}
|
|
9738
|
+
if ((input.supportRef.kind === "repo-policy" || input.supportRef.kind === "state-observation") && input.sourceStatus !== "current") {
|
|
9739
|
+
return {
|
|
9740
|
+
delivery: "reference",
|
|
9741
|
+
freshness: "unknown",
|
|
9742
|
+
reason: "The supporting repository source has not been revalidated for this query."
|
|
9324
9743
|
};
|
|
9325
9744
|
}
|
|
9326
9745
|
return {
|
|
9746
|
+
delivery: "inline",
|
|
9327
9747
|
freshness: "verified",
|
|
9328
|
-
reason: `
|
|
9748
|
+
reason: `Runtime-eligible through ${input.supportRef.kind}.`
|
|
9749
|
+
};
|
|
9750
|
+
}
|
|
9751
|
+
function isDirectKnowledgeSupport(supportRef) {
|
|
9752
|
+
return supportRef !== undefined && supportRef !== null && supportRef.kind !== "semantic-inference";
|
|
9753
|
+
}
|
|
9754
|
+
function normalizeNow(value) {
|
|
9755
|
+
if (value instanceof Date && Number.isFinite(value.getTime()))
|
|
9756
|
+
return value;
|
|
9757
|
+
if (typeof value === "string" && Number.isFinite(Date.parse(value)))
|
|
9758
|
+
return new Date(value);
|
|
9759
|
+
return new Date;
|
|
9760
|
+
}
|
|
9761
|
+
function isDue(value, now) {
|
|
9762
|
+
const timestamp = Date.parse(value);
|
|
9763
|
+
return Number.isFinite(timestamp) && timestamp <= now.getTime();
|
|
9764
|
+
}
|
|
9765
|
+
|
|
9766
|
+
// packages/core/src/evolution/knowledge/freshness.ts
|
|
9767
|
+
function deriveOkfKnowledgeFreshness(input) {
|
|
9768
|
+
const eligibility = resolveOkfKnowledgeRuntimeEligibility({
|
|
9769
|
+
reviewState: input.concept.reviewState,
|
|
9770
|
+
lifecycle: input.concept.lifecycle,
|
|
9771
|
+
supportRef: input.concept.supportRef ?? null,
|
|
9772
|
+
now: input.now,
|
|
9773
|
+
sourceStatus: input.repositoryFingerprintChanged === true ? "changed" : input.repositoryFingerprintChanged === false ? "current" : undefined,
|
|
9774
|
+
verifiedContradiction: input.verifiedContradiction
|
|
9775
|
+
});
|
|
9776
|
+
return {
|
|
9777
|
+
freshness: eligibility.freshness,
|
|
9778
|
+
reason: eligibility.reason
|
|
9329
9779
|
};
|
|
9330
9780
|
}
|
|
9331
9781
|
function createOkfKnowledgeVerificationSnapshot(input) {
|
|
9332
|
-
|
|
9333
|
-
if (!hasVerification)
|
|
9782
|
+
if (!isDirectKnowledgeSupport(input.candidate.supportRef))
|
|
9334
9783
|
return null;
|
|
9335
9784
|
const verifiedAt = normalizeIso(input.verifiedAt);
|
|
9336
9785
|
if (verifiedAt === null)
|
|
@@ -9342,20 +9791,6 @@ function createOkfKnowledgeVerificationSnapshot(input) {
|
|
|
9342
9791
|
repository: null
|
|
9343
9792
|
};
|
|
9344
9793
|
}
|
|
9345
|
-
function normalizeNow(value) {
|
|
9346
|
-
if (value instanceof Date && Number.isFinite(value.getTime()))
|
|
9347
|
-
return value;
|
|
9348
|
-
if (typeof value === "string") {
|
|
9349
|
-
const normalized = normalizeIso(value);
|
|
9350
|
-
if (normalized !== null)
|
|
9351
|
-
return new Date(normalized);
|
|
9352
|
-
}
|
|
9353
|
-
return new Date;
|
|
9354
|
-
}
|
|
9355
|
-
function isDue(value, now) {
|
|
9356
|
-
const normalized = normalizeIso(value);
|
|
9357
|
-
return normalized !== null && Date.parse(normalized) <= now.getTime();
|
|
9358
|
-
}
|
|
9359
9794
|
function normalizeIso(value) {
|
|
9360
9795
|
const time2 = Date.parse(value);
|
|
9361
9796
|
return Number.isFinite(time2) ? new Date(time2).toISOString() : null;
|
|
@@ -9731,6 +10166,7 @@ function normalizePlanCandidate(value, path) {
|
|
|
9731
10166
|
scores: normalizeScores(input.scores, `${path}.scores`),
|
|
9732
10167
|
decisionReason: sanitizeOkfText(readRequiredString(input.decisionReason, `${path}.decisionReason`)),
|
|
9733
10168
|
evidenceRefs: readStringArray(input.evidenceRefs, `${path}.evidenceRefs`).map(sanitizeOkfText),
|
|
10169
|
+
supportRef: input.supportRef === undefined ? undefined : normalizeKnowledgeSupportRef(input.supportRef, `${path}.supportRef`),
|
|
9734
10170
|
reviewState: parseOkfReviewState(readRequiredString(input.reviewState, `${path}.reviewState`)),
|
|
9735
10171
|
verificationNotApplicableReason: typeof input.verificationNotApplicableReason === "string" ? sanitizeOkfText(input.verificationNotApplicableReason) : undefined,
|
|
9736
10172
|
evalSetRefs: input.evalSetRefs === undefined ? undefined : readStringArray(input.evalSetRefs, `${path}.evalSetRefs`).map(sanitizeOkfText),
|
|
@@ -9790,6 +10226,7 @@ function validateCandidateContract(candidate, path, evalSetIds, add, options) {
|
|
|
9790
10226
|
validateStringArrayValue(candidate.pathScopes, `${path}.pathScopes`, add);
|
|
9791
10227
|
validateStringArrayValue(candidate.relatedConceptLinks, `${path}.relatedConceptLinks`, add);
|
|
9792
10228
|
validateStringArrayValue(candidate.evidenceRefs, `${path}.evidenceRefs`, add);
|
|
10229
|
+
validateKnowledgeSupportRefValue(candidate.supportRef, `${path}.supportRef`, add);
|
|
9793
10230
|
validateOverlayUpdatesValue(candidate.overlayUpdates, `${path}.overlayUpdates`, add);
|
|
9794
10231
|
validateScoresValue(candidate.scores, `${path}.scores`, add);
|
|
9795
10232
|
validatePrivacyCheckValue(candidate.privacyCheck, `${path}.privacyCheck`, add);
|
|
@@ -10004,6 +10441,24 @@ function normalizeBasis(value) {
|
|
|
10004
10441
|
throw new Error("Candidate basis is invalid.");
|
|
10005
10442
|
return value;
|
|
10006
10443
|
}
|
|
10444
|
+
function normalizeKnowledgeSupportRef(value, path) {
|
|
10445
|
+
const input = assertRecordValue(value, path);
|
|
10446
|
+
const kind = readRequiredString(input.kind, `${path}.kind`);
|
|
10447
|
+
if (kind !== "user-declaration" && kind !== "repo-policy" && kind !== "state-observation" && kind !== "verified-outcome" && kind !== "semantic-inference") {
|
|
10448
|
+
throw new Error(`${path}.kind is invalid.`);
|
|
10449
|
+
}
|
|
10450
|
+
const observedAt = readRequiredString(input.observedAt, `${path}.observedAt`);
|
|
10451
|
+
if (!Number.isFinite(Date.parse(observedAt)))
|
|
10452
|
+
throw new Error(`${path}.observedAt is invalid.`);
|
|
10453
|
+
const subjectFingerprint = input.subjectFingerprint === null ? null : sanitizeOkfText(readRequiredString(input.subjectFingerprint, `${path}.subjectFingerprint`));
|
|
10454
|
+
return {
|
|
10455
|
+
id: sanitizeOkfText(readRequiredString(input.id, `${path}.id`)),
|
|
10456
|
+
kind,
|
|
10457
|
+
sourceRefId: sanitizeOkfText(readRequiredString(input.sourceRefId, `${path}.sourceRefId`)),
|
|
10458
|
+
subjectFingerprint,
|
|
10459
|
+
observedAt: new Date(observedAt).toISOString()
|
|
10460
|
+
};
|
|
10461
|
+
}
|
|
10007
10462
|
function normalizeEvalSetDecision(value) {
|
|
10008
10463
|
const normalized = value === "no-write" ? "no_write" : value;
|
|
10009
10464
|
if (normalized !== "create" && normalized !== "needs-human" && normalized !== "no_write" && normalized !== "skip") {
|
|
@@ -10126,6 +10581,28 @@ function validateStringArrayValue(value, path, add) {
|
|
|
10126
10581
|
add(`${path}[${index}]`, "array.string", "Array items must be non-empty strings.");
|
|
10127
10582
|
});
|
|
10128
10583
|
}
|
|
10584
|
+
function validateKnowledgeSupportRefValue(value, path, add) {
|
|
10585
|
+
if (value === undefined)
|
|
10586
|
+
return;
|
|
10587
|
+
if (!isRecord7(value)) {
|
|
10588
|
+
add(path, "supportRef.object", "Candidate supportRef must be an object.");
|
|
10589
|
+
return;
|
|
10590
|
+
}
|
|
10591
|
+
if (!isNonEmptyString2(value.id))
|
|
10592
|
+
add(`${path}.id`, "supportRef.id", "supportRef id is required.");
|
|
10593
|
+
if (value.kind !== "user-declaration" && value.kind !== "repo-policy" && value.kind !== "state-observation" && value.kind !== "verified-outcome" && value.kind !== "semantic-inference") {
|
|
10594
|
+
add(`${path}.kind`, "supportRef.kind", "supportRef kind is invalid.");
|
|
10595
|
+
}
|
|
10596
|
+
if (!isNonEmptyString2(value.sourceRefId)) {
|
|
10597
|
+
add(`${path}.sourceRefId`, "supportRef.sourceRefId", "supportRef sourceRefId is required.");
|
|
10598
|
+
}
|
|
10599
|
+
if (value.subjectFingerprint !== null && !isNonEmptyString2(value.subjectFingerprint)) {
|
|
10600
|
+
add(`${path}.subjectFingerprint`, "supportRef.subjectFingerprint", "supportRef subjectFingerprint must be null or a non-empty string.");
|
|
10601
|
+
}
|
|
10602
|
+
if (!isNonEmptyString2(value.observedAt) || !Number.isFinite(Date.parse(value.observedAt))) {
|
|
10603
|
+
add(`${path}.observedAt`, "supportRef.observedAt", "supportRef observedAt must be an ISO timestamp.");
|
|
10604
|
+
}
|
|
10605
|
+
}
|
|
10129
10606
|
function validateOverlayUpdatesValue(value, path, add) {
|
|
10130
10607
|
if (!Array.isArray(value)) {
|
|
10131
10608
|
add(path, "overlayUpdates.array", "Overlay updates must be an array.");
|
|
@@ -10329,7 +10806,7 @@ function decideOkfKnowledgeCandidate(candidate, context) {
|
|
|
10329
10806
|
const localActiveTarget = sanitized.targetStore === "okf" || sanitized.kind === "evos-case";
|
|
10330
10807
|
const hasScopeTags = sanitized.repoTags.length > 0 && sanitized.roleTags.length > 0;
|
|
10331
10808
|
const highRiskRequiresHuman = hasHighRiskHumanReviewSignal(candidate) || hasHighRiskHumanReviewSignal(sanitized);
|
|
10332
|
-
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;
|
|
10809
|
+
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;
|
|
10333
10810
|
if (autoAcceptEligible) {
|
|
10334
10811
|
return {
|
|
10335
10812
|
...sanitized,
|
|
@@ -10656,7 +11133,7 @@ async function preflightOkfConceptTargets(okfDir, candidates) {
|
|
|
10656
11133
|
targets.set(targetPath, { action: "write", candidateId: candidate.id });
|
|
10657
11134
|
continue;
|
|
10658
11135
|
}
|
|
10659
|
-
const existing = parseOkfConceptFile(okfDir, targetPath, await
|
|
11136
|
+
const existing = parseOkfConceptFile(okfDir, targetPath, await readFile11(targetPath, "utf8"));
|
|
10660
11137
|
if (existing === null) {
|
|
10661
11138
|
throw new Error(`OKF concept target is occupied by an unrecognized file: ${candidate.targetPath}`);
|
|
10662
11139
|
}
|
|
@@ -10707,7 +11184,7 @@ async function writeFailedOkfKnowledgePlanArtifact(input) {
|
|
|
10707
11184
|
}
|
|
10708
11185
|
async function readFailedOkfKnowledgePlanArtifact(input) {
|
|
10709
11186
|
const path = resolveFailedPlanPath(input.homeDir, input.projectKey, input.runId);
|
|
10710
|
-
const value = JSON.parse(await
|
|
11187
|
+
const value = JSON.parse(await readFile11(path, "utf8"));
|
|
10711
11188
|
if (isRecord7(value) && value.kind === "okf-knowledge-failed-plan") {
|
|
10712
11189
|
const artifact = value;
|
|
10713
11190
|
validateFailedPlanArtifact(artifact);
|
|
@@ -10893,7 +11370,7 @@ async function listOkfKnowledgeConcepts(input) {
|
|
|
10893
11370
|
for (const file of files) {
|
|
10894
11371
|
if (RESERVED_OKF_FILENAMES.has(file.name))
|
|
10895
11372
|
continue;
|
|
10896
|
-
const content = await
|
|
11373
|
+
const content = await readFile11(file.path, "utf8");
|
|
10897
11374
|
const parsed = parseOkfConceptFile(okfDir, file.path, content);
|
|
10898
11375
|
if (parsed !== null)
|
|
10899
11376
|
concepts.push(parsed);
|
|
@@ -10932,7 +11409,7 @@ async function queryOkfKnowledge(input) {
|
|
|
10932
11409
|
for (const file of files) {
|
|
10933
11410
|
if (RESERVED_OKF_FILENAMES.has(file.name))
|
|
10934
11411
|
continue;
|
|
10935
|
-
const content = await
|
|
11412
|
+
const content = await readFile11(file.path, "utf8");
|
|
10936
11413
|
const sourceLink = toOkfLink(okfDir, file.path);
|
|
10937
11414
|
if (isUnsafeOkfQueryContent(content)) {
|
|
10938
11415
|
warnings.push(`Omitted unsafe OKF knowledge item: ${sourceLink}. Run evodev knowledge lint.`);
|
|
@@ -11209,8 +11686,8 @@ function mergeAcceptedEvosCasesIntoKnowledgeQuery(result, cases, warnings = [],
|
|
|
11209
11686
|
score: structuredScore + lexical.score,
|
|
11210
11687
|
title: evosCase.title,
|
|
11211
11688
|
summary: evosCase.expectedFutureBehavior || evosCase.result.summary || evosCase.title,
|
|
11212
|
-
freshness: "
|
|
11213
|
-
runtimeExcerpt:
|
|
11689
|
+
freshness: "unknown",
|
|
11690
|
+
runtimeExcerpt: null,
|
|
11214
11691
|
matchReasons: lexical.reasons.length === 0 ? structuredReasons : [...structuredReasons, ...lexical.reasons]
|
|
11215
11692
|
}
|
|
11216
11693
|
];
|
|
@@ -11223,8 +11700,12 @@ function mergeAcceptedEvosCasesIntoKnowledgeQuery(result, cases, warnings = [],
|
|
|
11223
11700
|
};
|
|
11224
11701
|
}
|
|
11225
11702
|
async function createScopedKnowledgeContextPack(input) {
|
|
11226
|
-
const result = await queryScopedOkfKnowledgeContext(
|
|
11227
|
-
|
|
11703
|
+
const result = await queryScopedOkfKnowledgeContext({
|
|
11704
|
+
...input,
|
|
11705
|
+
limit: input.inlineOnly === true ? Math.max(input.limit ?? 3, 50) : input.limit
|
|
11706
|
+
});
|
|
11707
|
+
const selectedItems = input.inlineOnly === true ? result.items.filter((item) => item.runtimeExcerpt !== null).slice(0, input.limit ?? 3) : result.items;
|
|
11708
|
+
if (selectedItems.length === 0)
|
|
11228
11709
|
return null;
|
|
11229
11710
|
const scope = {
|
|
11230
11711
|
...result.projectKey === undefined ? {} : { projectKey: result.projectKey },
|
|
@@ -11232,7 +11713,7 @@ async function createScopedKnowledgeContextPack(input) {
|
|
|
11232
11713
|
...result.workflowId === undefined ? {} : { workflowId: result.workflowId },
|
|
11233
11714
|
paths: result.paths
|
|
11234
11715
|
};
|
|
11235
|
-
const items =
|
|
11716
|
+
const items = selectedItems.map((item) => ({
|
|
11236
11717
|
id: item.id,
|
|
11237
11718
|
sourceType: item.sourceType,
|
|
11238
11719
|
sourceLink: item.sourceLink,
|
|
@@ -11249,10 +11730,11 @@ async function createScopedKnowledgeContextPack(input) {
|
|
|
11249
11730
|
}));
|
|
11250
11731
|
const okfIndexRevision = createKnowledgeContextRevision(items);
|
|
11251
11732
|
const queryText = normalizeKnowledgeQueryText(result.queryText);
|
|
11733
|
+
const queryHash = queryText === undefined ? undefined : sha256Short2(queryText);
|
|
11252
11734
|
const packSeed = stableJsonStringify2({
|
|
11253
11735
|
okfIndexRevision,
|
|
11254
11736
|
scope,
|
|
11255
|
-
|
|
11737
|
+
queryHash: queryHash ?? null,
|
|
11256
11738
|
items: items.map((item) => ({
|
|
11257
11739
|
id: item.id,
|
|
11258
11740
|
sourceType: item.sourceType,
|
|
@@ -11270,7 +11752,7 @@ async function createScopedKnowledgeContextPack(input) {
|
|
|
11270
11752
|
id: `ctxpack-${sha256Short2(packSeed)}`,
|
|
11271
11753
|
okfIndexRevision,
|
|
11272
11754
|
scope,
|
|
11273
|
-
...
|
|
11755
|
+
...queryHash === undefined ? {} : { queryHash },
|
|
11274
11756
|
items,
|
|
11275
11757
|
warnings: result.warnings.length === 0 ? [] : [
|
|
11276
11758
|
"Some scoped knowledge items were omitted because they failed safety validation; run evodev knowledge lint."
|
|
@@ -11285,7 +11767,6 @@ function formatScopedKnowledgePromptBlock(pack) {
|
|
|
11285
11767
|
`Project: ${pack.scope.projectKey ?? "all"}`,
|
|
11286
11768
|
`Role: ${pack.scope.roleId ?? "any"}`,
|
|
11287
11769
|
`Workflow: ${pack.scope.workflowId ?? "any"}`,
|
|
11288
|
-
...pack.queryText === undefined ? [] : [`Query: ${sanitizeOkfText(pack.queryText)}`],
|
|
11289
11770
|
`Paths: ${pack.scope.paths.length === 0 ? "all" : pack.scope.paths.join(", ")}`,
|
|
11290
11771
|
"Raw content stored: false",
|
|
11291
11772
|
"Freshness is determined by EvoDev lifecycle and verification metadata; do not infer validity from timestamps.",
|
|
@@ -11323,9 +11804,11 @@ async function writeContextInjectionReceipt(input) {
|
|
|
11323
11804
|
okfIndexRevision: input.pack.okfIndexRevision,
|
|
11324
11805
|
scope: input.pack.scope,
|
|
11325
11806
|
itemIds: input.pack.items.map((item) => item.id),
|
|
11807
|
+
queryHash: input.pack.queryHash ?? null,
|
|
11326
11808
|
injectedAt: input.injectedAt ?? new Date().toISOString(),
|
|
11327
11809
|
hookEventId: input.hookEventId ?? null,
|
|
11328
11810
|
trigger: input.trigger,
|
|
11811
|
+
outcome: null,
|
|
11329
11812
|
rawContentStored: false
|
|
11330
11813
|
};
|
|
11331
11814
|
const path = resolveContextInjectionReceiptPath({
|
|
@@ -11336,6 +11819,42 @@ async function writeContextInjectionReceipt(input) {
|
|
|
11336
11819
|
await writeJson3(path, receipt, { overwrite: true });
|
|
11337
11820
|
return { path, receipt };
|
|
11338
11821
|
}
|
|
11822
|
+
async function recordContextInjectionOutcome(input) {
|
|
11823
|
+
const stateDir = resolveEvoDevPaths(input.homeDir).stateDir;
|
|
11824
|
+
const directory = join13(stateDir, "context-injections", sanitizeReceiptPathSegment(input.sessionKey));
|
|
11825
|
+
let names;
|
|
11826
|
+
try {
|
|
11827
|
+
names = await readdir8(directory);
|
|
11828
|
+
} catch {
|
|
11829
|
+
return [];
|
|
11830
|
+
}
|
|
11831
|
+
const paths2 = [];
|
|
11832
|
+
for (const name of names.filter((item) => item.endsWith(".json")).sort()) {
|
|
11833
|
+
const path = join13(directory, name);
|
|
11834
|
+
let receipt;
|
|
11835
|
+
try {
|
|
11836
|
+
receipt = JSON.parse(await readFile11(path, "utf8"));
|
|
11837
|
+
} catch {
|
|
11838
|
+
continue;
|
|
11839
|
+
}
|
|
11840
|
+
if (receipt.version !== 1 || receipt.rawContentStored !== false || receipt.outcome !== null && receipt.outcome !== undefined) {
|
|
11841
|
+
continue;
|
|
11842
|
+
}
|
|
11843
|
+
const updated = {
|
|
11844
|
+
...receipt,
|
|
11845
|
+
outcome: {
|
|
11846
|
+
status: "verified",
|
|
11847
|
+
eventId: sanitizeReceiptPathSegment(input.eventId),
|
|
11848
|
+
observedAt: normalizeTimestamp(input.observedAt),
|
|
11849
|
+
summaryHash: sha256Short2(input.summary),
|
|
11850
|
+
rawContentStored: false
|
|
11851
|
+
}
|
|
11852
|
+
};
|
|
11853
|
+
await writeJson3(path, updated, { overwrite: true });
|
|
11854
|
+
paths2.push(path);
|
|
11855
|
+
}
|
|
11856
|
+
return paths2;
|
|
11857
|
+
}
|
|
11339
11858
|
async function markOkfKnowledgeConceptStale(input) {
|
|
11340
11859
|
const target = await readMutableOkfConcept(input.homeDir, input.conceptId);
|
|
11341
11860
|
const lifecycle = {
|
|
@@ -11432,7 +11951,7 @@ async function readMutableOkfConcept(homeDir, conceptId) {
|
|
|
11432
11951
|
for (const file of files) {
|
|
11433
11952
|
if (RESERVED_OKF_FILENAMES.has(file.name))
|
|
11434
11953
|
continue;
|
|
11435
|
-
const content = await
|
|
11954
|
+
const content = await readFile11(file.path, "utf8");
|
|
11436
11955
|
const concept = parseOkfConceptFile(paths2.okfDir, file.path, content);
|
|
11437
11956
|
if (concept === null)
|
|
11438
11957
|
continue;
|
|
@@ -11544,7 +12063,7 @@ async function lintOkfKnowledge(input) {
|
|
|
11544
12063
|
const knownConceptIds = new Set(files.filter((file) => !RESERVED_OKF_FILENAMES.has(file.name)).map((file) => toOkfRelativePath(paths2.okfDir, file.path).replace(/\.md$/, "")));
|
|
11545
12064
|
for (const file of files) {
|
|
11546
12065
|
const rel = displayOkfPath(paths2.okfDir, file.path);
|
|
11547
|
-
const content = await
|
|
12066
|
+
const content = await readFile11(file.path, "utf8");
|
|
11548
12067
|
if (file.name === "index.md") {
|
|
11549
12068
|
if (file.path !== join13(paths2.okfDir, "index.md") && hasFrontmatter(content)) {
|
|
11550
12069
|
errors2.push(`Non-root index.md must not have frontmatter: ${rel}`);
|
|
@@ -11890,6 +12409,9 @@ function explainAutomaticNoWriteDecision(candidate, scores, hasVerification, has
|
|
|
11890
12409
|
reasons.push("target is not user-local OKF");
|
|
11891
12410
|
if (candidate.basis !== "direct")
|
|
11892
12411
|
reasons.push("basis is inferred");
|
|
12412
|
+
if (!isDirectKnowledgeSupport(candidate.supportRef)) {
|
|
12413
|
+
reasons.push("structured direct support is missing");
|
|
12414
|
+
}
|
|
11893
12415
|
if (!candidate.metadataOnlyEvidence)
|
|
11894
12416
|
reasons.push("evidence is not metadata-only");
|
|
11895
12417
|
if (!hasVerification)
|
|
@@ -12033,7 +12555,7 @@ async function ensureOverlayConcept(input) {
|
|
|
12033
12555
|
const exists = await pathExists3(input.overlayPath);
|
|
12034
12556
|
const linkLine = `- [${input.candidate.title}](${input.link}) - ${input.candidate.description}`;
|
|
12035
12557
|
if (exists) {
|
|
12036
|
-
const current = await
|
|
12558
|
+
const current = await readFile11(input.overlayPath, "utf8");
|
|
12037
12559
|
if (current.includes(input.link))
|
|
12038
12560
|
return;
|
|
12039
12561
|
await writeFile7(input.overlayPath, `${current.trimEnd()}
|
|
@@ -12140,6 +12662,7 @@ function renderOkfConcept(candidate, plan, existing) {
|
|
|
12140
12662
|
` stableKey: ${yamlString(candidate.stableKey)}`,
|
|
12141
12663
|
` reviewState: ${yamlString(reviewState)}`,
|
|
12142
12664
|
renderLifecycleYaml(" ", lifecycle),
|
|
12665
|
+
renderKnowledgeSupportRefYaml(" ", candidate.supportRef ?? null),
|
|
12143
12666
|
renderVerificationSnapshotYaml(" ", verificationSnapshot),
|
|
12144
12667
|
" source:",
|
|
12145
12668
|
` kind: ${yamlString("trace-distillation")}`,
|
|
@@ -12257,7 +12780,7 @@ async function regenerateOkfDirectoryIndexes(okfDir) {
|
|
|
12257
12780
|
continue;
|
|
12258
12781
|
}
|
|
12259
12782
|
const filePath = join13(directory, entry.name);
|
|
12260
|
-
const concept = parseOkfConceptFile(okfDir, filePath, await
|
|
12783
|
+
const concept = parseOkfConceptFile(okfDir, filePath, await readFile11(filePath, "utf8"));
|
|
12261
12784
|
if (concept !== null && !isActiveOkfConcept(concept))
|
|
12262
12785
|
continue;
|
|
12263
12786
|
const title = concept?.title ?? titleFromSlug(entry.name.replace(/\.md$/, ""));
|
|
@@ -12313,7 +12836,7 @@ async function appendOkfLog(okfDir, entry) {
|
|
|
12313
12836
|
async function prependLogEntry(logPath, entry) {
|
|
12314
12837
|
await mkdir8(dirname9(logPath), { recursive: true });
|
|
12315
12838
|
const date = todayIsoDate();
|
|
12316
|
-
const existing = await pathExists3(logPath) ? await
|
|
12839
|
+
const existing = await pathExists3(logPath) ? await readFile11(logPath, "utf8") : `# Directory Update Log
|
|
12317
12840
|
`;
|
|
12318
12841
|
const line = `* ${entry}`;
|
|
12319
12842
|
if (existing.includes(`## ${date}`)) {
|
|
@@ -12363,6 +12886,7 @@ function parseOkfConceptFile(okfDir, filePath, content) {
|
|
|
12363
12886
|
lifecycle: lifecycleParsed.lifecycle,
|
|
12364
12887
|
lifecyclePersisted: lifecycleParsed.persisted,
|
|
12365
12888
|
verificationSnapshot: parseOkfVerificationSnapshot(frontmatter),
|
|
12889
|
+
supportRef: parseOkfKnowledgeSupportRef(frontmatter),
|
|
12366
12890
|
title,
|
|
12367
12891
|
description,
|
|
12368
12892
|
tags,
|
|
@@ -12373,6 +12897,22 @@ function parseOkfConceptFile(okfDir, filePath, content) {
|
|
|
12373
12897
|
body: parsed.body
|
|
12374
12898
|
};
|
|
12375
12899
|
}
|
|
12900
|
+
function parseOkfKnowledgeSupportRef(frontmatter) {
|
|
12901
|
+
const block = extractNestedYamlBlock(frontmatter, "evodev", "supportRef");
|
|
12902
|
+
if (block === null)
|
|
12903
|
+
return null;
|
|
12904
|
+
try {
|
|
12905
|
+
return normalizeKnowledgeSupportRef({
|
|
12906
|
+
id: readIndentedYamlScalar(block, "id"),
|
|
12907
|
+
kind: readIndentedYamlScalar(block, "kind"),
|
|
12908
|
+
sourceRefId: readIndentedYamlScalar(block, "sourceRefId"),
|
|
12909
|
+
subjectFingerprint: readNullableLifecycleString(readIndentedYamlScalar(block, "subjectFingerprint")),
|
|
12910
|
+
observedAt: readIndentedYamlScalar(block, "observedAt")
|
|
12911
|
+
}, "evodev.supportRef");
|
|
12912
|
+
} catch {
|
|
12913
|
+
return null;
|
|
12914
|
+
}
|
|
12915
|
+
}
|
|
12376
12916
|
function parseOkfVerificationSnapshot(frontmatter) {
|
|
12377
12917
|
const block = extractNestedYamlBlock(frontmatter, "evodev", "verificationSnapshot");
|
|
12378
12918
|
if (block === null)
|
|
@@ -12946,6 +13486,19 @@ function renderVerificationSnapshotYaml(indent, snapshot) {
|
|
|
12946
13486
|
].join(`
|
|
12947
13487
|
`);
|
|
12948
13488
|
}
|
|
13489
|
+
function renderKnowledgeSupportRefYaml(indent, supportRef) {
|
|
13490
|
+
if (supportRef === null)
|
|
13491
|
+
return `${indent}supportRef: null`;
|
|
13492
|
+
return [
|
|
13493
|
+
`${indent}supportRef:`,
|
|
13494
|
+
`${indent} id: ${yamlString(supportRef.id)}`,
|
|
13495
|
+
`${indent} kind: ${yamlString(supportRef.kind)}`,
|
|
13496
|
+
`${indent} sourceRefId: ${yamlString(supportRef.sourceRefId)}`,
|
|
13497
|
+
`${indent} subjectFingerprint: ${yamlNullableString(supportRef.subjectFingerprint)}`,
|
|
13498
|
+
`${indent} observedAt: ${yamlString(supportRef.observedAt)}`
|
|
13499
|
+
].join(`
|
|
13500
|
+
`);
|
|
13501
|
+
}
|
|
12949
13502
|
function lifecycleStatusFromReviewState(reviewState) {
|
|
12950
13503
|
if (reviewState === "stale")
|
|
12951
13504
|
return "stale";
|
|
@@ -13111,7 +13664,7 @@ async function createLegacyGeneratedKnowledgeCleanupPlan(homeDir, generatedAt) {
|
|
|
13111
13664
|
for (const file of await listMarkdownFiles(conceptRoot)) {
|
|
13112
13665
|
if (RESERVED_OKF_FILENAMES.has(file.name))
|
|
13113
13666
|
continue;
|
|
13114
|
-
const content = await
|
|
13667
|
+
const content = await readFile11(file.path, "utf8");
|
|
13115
13668
|
if (!isLegacyGeneratedOkfConcept(file.name, content))
|
|
13116
13669
|
continue;
|
|
13117
13670
|
concepts.push(createLegacyCleanupArtifact({
|
|
@@ -13124,7 +13677,7 @@ async function createLegacyGeneratedKnowledgeCleanupPlan(homeDir, generatedAt) {
|
|
|
13124
13677
|
}
|
|
13125
13678
|
const evosCases = [];
|
|
13126
13679
|
for (const file of await listJsonFiles2(evoDevPaths.evosCasesDir)) {
|
|
13127
|
-
const content = await
|
|
13680
|
+
const content = await readFile11(file.path, "utf8");
|
|
13128
13681
|
if (!isLegacyGeneratedEvosCase(content))
|
|
13129
13682
|
continue;
|
|
13130
13683
|
evosCases.push(createLegacyCleanupArtifact({
|
|
@@ -13145,7 +13698,7 @@ async function createLegacyGeneratedKnowledgeCleanupPlan(homeDir, generatedAt) {
|
|
|
13145
13698
|
for (const file of await listMarkdownFiles(join13(okfPaths.okfDir, root))) {
|
|
13146
13699
|
if (RESERVED_OKF_FILENAMES.has(file.name))
|
|
13147
13700
|
continue;
|
|
13148
|
-
const content = await
|
|
13701
|
+
const content = await readFile11(file.path, "utf8");
|
|
13149
13702
|
const cleanedContent = removeLegacyOverlayLinks(content, conceptLinks);
|
|
13150
13703
|
if (cleanedContent === content)
|
|
13151
13704
|
continue;
|
|
@@ -13458,7 +14011,7 @@ function parseAgentProfile(value) {
|
|
|
13458
14011
|
return { ...profile, permissions };
|
|
13459
14012
|
}
|
|
13460
14013
|
async function readAgentProfile(path) {
|
|
13461
|
-
return parseAgentProfile(JSON.parse(await
|
|
14014
|
+
return parseAgentProfile(JSON.parse(await readFile12(path, "utf8")));
|
|
13462
14015
|
}
|
|
13463
14016
|
function loadAgentContextDryRun(profile) {
|
|
13464
14017
|
const parsed = parseAgentProfile(profile);
|
|
@@ -13635,7 +14188,7 @@ function expectRelativePath(value, path) {
|
|
|
13635
14188
|
return text2;
|
|
13636
14189
|
}
|
|
13637
14190
|
// packages/core/src/assets/scanner.ts
|
|
13638
|
-
import { readFile as
|
|
14191
|
+
import { readFile as readFile13, readdir as readdir9, stat as stat6 } from "node:fs/promises";
|
|
13639
14192
|
import { join as join14, relative as relative5 } from "node:path";
|
|
13640
14193
|
async function scanAssets(paths2) {
|
|
13641
14194
|
const [skills, agents] = await Promise.all([
|
|
@@ -13692,7 +14245,7 @@ async function readScannedAsset(rootDir, manifestPath, parseManifest) {
|
|
|
13692
14245
|
async function readManifestJson(manifestPath) {
|
|
13693
14246
|
let raw;
|
|
13694
14247
|
try {
|
|
13695
|
-
raw = await
|
|
14248
|
+
raw = await readFile13(manifestPath, "utf8");
|
|
13696
14249
|
} catch (error) {
|
|
13697
14250
|
throw new EvoDevAssetError(`Cannot read manifest (${describeFileError(error)})`, manifestPath);
|
|
13698
14251
|
}
|
|
@@ -13751,7 +14304,7 @@ function isNodeError(error) {
|
|
|
13751
14304
|
return error instanceof Error && "code" in error;
|
|
13752
14305
|
}
|
|
13753
14306
|
// packages/core/src/code-agent-traces/index.ts
|
|
13754
|
-
import { mkdir as mkdir9, readFile as
|
|
14307
|
+
import { mkdir as mkdir9, readFile as readFile14, readdir as readdir10, stat as stat7, writeFile as writeFile8 } from "node:fs/promises";
|
|
13755
14308
|
import { dirname as dirname10, isAbsolute as isAbsolute6, join as join15, normalize, relative as relative6 } from "node:path";
|
|
13756
14309
|
var CODE_AGENT_TRACE_TARGETS = ["claude", "codex"];
|
|
13757
14310
|
var CODE_AGENT_TRACE_REF_SOURCES = [
|
|
@@ -13771,7 +14324,7 @@ function resolveCodeAgentTraceRefPaths(homeDir) {
|
|
|
13771
14324
|
};
|
|
13772
14325
|
}
|
|
13773
14326
|
function createCodeAgentTraceRef(input) {
|
|
13774
|
-
const
|
|
14327
|
+
const sessionKey2 = sanitizePersistentIdentifier2(input.sessionKey, "session");
|
|
13775
14328
|
const nativeSessionId = input.nativeSessionId ?? null;
|
|
13776
14329
|
const nativeSessionIdHash = input.nativeSessionIdHash === undefined ? hashOptionalIdentifier2(nativeSessionId) : sanitizeHashMetadata(input.nativeSessionIdHash);
|
|
13777
14330
|
const cwdHash = input.cwdHash === undefined ? hashOptionalIdentifier2(input.cwd ?? null) : sanitizeHashMetadata(input.cwdHash);
|
|
@@ -13782,9 +14335,9 @@ function createCodeAgentTraceRef(input) {
|
|
|
13782
14335
|
});
|
|
13783
14336
|
return {
|
|
13784
14337
|
version: 1,
|
|
13785
|
-
id: `trace-ref-${input.target}-${
|
|
14338
|
+
id: `trace-ref-${input.target}-${sessionKey2}`,
|
|
13786
14339
|
target: input.target,
|
|
13787
|
-
sessionKey,
|
|
14340
|
+
sessionKey: sessionKey2,
|
|
13788
14341
|
nativeSessionIdHash,
|
|
13789
14342
|
projectKey: input.projectKey === undefined || input.projectKey === null ? null : sanitizePersistentIdentifier2(input.projectKey, "project"),
|
|
13790
14343
|
runId: input.runId === undefined || input.runId === null ? null : sanitizePersistentIdentifier2(input.runId, "run"),
|
|
@@ -13852,7 +14405,7 @@ async function readCodeAgentTraceRef(input) {
|
|
|
13852
14405
|
target: targetAndSession.target,
|
|
13853
14406
|
sessionKey: targetAndSession.sessionKey
|
|
13854
14407
|
});
|
|
13855
|
-
const ref = sanitizeCodeAgentTraceRefForWrite(parseCodeAgentTraceRef(JSON.parse(await
|
|
14408
|
+
const ref = sanitizeCodeAgentTraceRefForWrite(parseCodeAgentTraceRef(JSON.parse(await readFile14(path, "utf8"))));
|
|
13856
14409
|
if (ref.id !== input.id)
|
|
13857
14410
|
throw new Error(`Code Agent trace ref id mismatch: ${input.id}`);
|
|
13858
14411
|
return { ref, path };
|
|
@@ -13867,7 +14420,7 @@ async function listCodeAgentTraceRefs(input) {
|
|
|
13867
14420
|
if (!entry.isFile() || !entry.name.endsWith(".json"))
|
|
13868
14421
|
continue;
|
|
13869
14422
|
const path = join15(dir, entry.name);
|
|
13870
|
-
const ref = sanitizeCodeAgentTraceRefForWrite(parseCodeAgentTraceRef(JSON.parse(await
|
|
14423
|
+
const ref = sanitizeCodeAgentTraceRefForWrite(parseCodeAgentTraceRef(JSON.parse(await readFile14(path, "utf8"))));
|
|
13871
14424
|
if (input.projectKey !== undefined && ref.projectKey !== input.projectKey)
|
|
13872
14425
|
continue;
|
|
13873
14426
|
if (input.runId !== undefined && ref.runId !== input.runId)
|
|
@@ -14228,7 +14781,7 @@ function expectNonNegativeInteger(value, path) {
|
|
|
14228
14781
|
return value;
|
|
14229
14782
|
}
|
|
14230
14783
|
// packages/core/src/config/store.ts
|
|
14231
|
-
import { mkdir as mkdir10, readFile as
|
|
14784
|
+
import { mkdir as mkdir10, readFile as readFile15, writeFile as writeFile9 } from "node:fs/promises";
|
|
14232
14785
|
import { dirname as dirname11 } from "node:path";
|
|
14233
14786
|
function createCoreConfigStore(homeDir) {
|
|
14234
14787
|
const paths2 = resolveEvoDevPaths(homeDir);
|
|
@@ -14352,7 +14905,7 @@ async function ensureKnowledgeBaseFiles(paths2) {
|
|
|
14352
14905
|
async function readJsonFile2(filePath, parse2) {
|
|
14353
14906
|
let raw;
|
|
14354
14907
|
try {
|
|
14355
|
-
raw = await
|
|
14908
|
+
raw = await readFile15(filePath, "utf8");
|
|
14356
14909
|
} catch (error) {
|
|
14357
14910
|
throw new EvoDevConfigError(`Cannot read config file (${describeFileError2(error)})`, filePath);
|
|
14358
14911
|
}
|
|
@@ -14384,7 +14937,7 @@ async function readJsonFileOrDefault(filePath, parse2, fallback) {
|
|
|
14384
14937
|
async function writeIndexIfMissingOrMigrate(filePath, kind, defaults) {
|
|
14385
14938
|
let raw;
|
|
14386
14939
|
try {
|
|
14387
|
-
raw = await
|
|
14940
|
+
raw = await readFile15(filePath, "utf8");
|
|
14388
14941
|
} catch (error) {
|
|
14389
14942
|
if (isNodeError2(error) && error.code === "ENOENT") {
|
|
14390
14943
|
await writeJsonFile3(filePath, defaults);
|
|
@@ -14407,7 +14960,7 @@ async function writeIndexIfMissingOrMigrate(filePath, kind, defaults) {
|
|
|
14407
14960
|
}
|
|
14408
14961
|
async function writeTextIfMissing2(filePath, value) {
|
|
14409
14962
|
try {
|
|
14410
|
-
await
|
|
14963
|
+
await readFile15(filePath, "utf8");
|
|
14411
14964
|
} catch (error) {
|
|
14412
14965
|
if (isNodeError2(error) && error.code === "ENOENT") {
|
|
14413
14966
|
await mkdir10(dirname11(filePath), { recursive: true });
|
|
@@ -14436,7 +14989,7 @@ function isRecord10(value) {
|
|
|
14436
14989
|
}
|
|
14437
14990
|
// packages/core/src/daemon/index.ts
|
|
14438
14991
|
import { randomBytes } from "node:crypto";
|
|
14439
|
-
import { mkdir as mkdir13, readFile as
|
|
14992
|
+
import { mkdir as mkdir13, readFile as readFile19, readdir as readdir13, rm as rm6, stat as stat10, writeFile as writeFile11 } from "node:fs/promises";
|
|
14440
14993
|
import { createServer } from "node:http";
|
|
14441
14994
|
import { dirname as dirname14, join as join20 } from "node:path";
|
|
14442
14995
|
|
|
@@ -14549,7 +15102,7 @@ function formatEvolutionReviewSnapshot(snapshot) {
|
|
|
14549
15102
|
`);
|
|
14550
15103
|
}
|
|
14551
15104
|
// packages/core/src/evolution/evidence/analysis.ts
|
|
14552
|
-
import { readFile as
|
|
15105
|
+
import { readFile as readFile16, readdir as readdir11 } from "node:fs/promises";
|
|
14553
15106
|
import { basename as basename4, join as join16 } from "node:path";
|
|
14554
15107
|
async function analyzeEvolutionRun(input) {
|
|
14555
15108
|
const projectKey = resolveEvolutionProjectKey(input);
|
|
@@ -14665,7 +15218,7 @@ async function analyzeEvolutionRun(input) {
|
|
|
14665
15218
|
};
|
|
14666
15219
|
}
|
|
14667
15220
|
async function readExecutionEvidenceEvents(input) {
|
|
14668
|
-
const raw = await
|
|
15221
|
+
const raw = await readFile16(input.path, "utf8");
|
|
14669
15222
|
const lines = raw.split(`
|
|
14670
15223
|
`);
|
|
14671
15224
|
const events = [];
|
|
@@ -14735,7 +15288,7 @@ function createEvidenceEventFromExecutionEvent(input) {
|
|
|
14735
15288
|
};
|
|
14736
15289
|
}
|
|
14737
15290
|
async function readTraceEvidenceEvents(input) {
|
|
14738
|
-
const raw = await
|
|
15291
|
+
const raw = await readFile16(input.path, "utf8");
|
|
14739
15292
|
const lines = raw.split(`
|
|
14740
15293
|
`);
|
|
14741
15294
|
const events = [];
|
|
@@ -15220,7 +15773,7 @@ function createProvenance(evidenceWindow, createdAt, sourceRefs) {
|
|
|
15220
15773
|
}
|
|
15221
15774
|
// packages/core/src/evolution/processor/process.ts
|
|
15222
15775
|
import { randomUUID as randomUUID3 } from "node:crypto";
|
|
15223
|
-
import { mkdir as mkdir11, open as open2, readFile as
|
|
15776
|
+
import { mkdir as mkdir11, open as open2, readFile as readFile17, rm as rm5, stat as stat8 } from "node:fs/promises";
|
|
15224
15777
|
import { dirname as dirname12, join as join18 } from "node:path";
|
|
15225
15778
|
var PROCESS_LOCK_STALE_MS2 = 5 * 60 * 1000;
|
|
15226
15779
|
var PROCESS_LOCK_HEARTBEAT_MS = 30 * 1000;
|
|
@@ -15646,7 +16199,7 @@ async function assertEvolutionProcessLockOwned(lock) {
|
|
|
15646
16199
|
}
|
|
15647
16200
|
async function evolutionProcessLockIsOwned(lock) {
|
|
15648
16201
|
try {
|
|
15649
|
-
const value = JSON.parse(await
|
|
16202
|
+
const value = JSON.parse(await readFile17(lock.path, "utf8"));
|
|
15650
16203
|
return value.ownerId === lock.ownerId;
|
|
15651
16204
|
} catch {
|
|
15652
16205
|
return false;
|
|
@@ -15709,7 +16262,7 @@ function isTransientEvolutionError(error) {
|
|
|
15709
16262
|
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);
|
|
15710
16263
|
}
|
|
15711
16264
|
// packages/core/src/observability/index.ts
|
|
15712
|
-
import { mkdir as mkdir12, readFile as
|
|
16265
|
+
import { mkdir as mkdir12, readFile as readFile18, readdir as readdir12, stat as stat9, writeFile as writeFile10 } from "node:fs/promises";
|
|
15713
16266
|
import { dirname as dirname13, join as join19 } from "node:path";
|
|
15714
16267
|
var EVENT_TYPE_TO_DIR = {
|
|
15715
16268
|
"verification.completed": "verification",
|
|
@@ -15761,7 +16314,7 @@ async function listObservabilityEvents(homeDir, type) {
|
|
|
15761
16314
|
const path = resolveObservabilityStorePaths(homeDir, eventType).eventsPath;
|
|
15762
16315
|
if (!await pathExists5(path))
|
|
15763
16316
|
continue;
|
|
15764
|
-
const lines = (await
|
|
16317
|
+
const lines = (await readFile18(path, "utf8")).split(`
|
|
15765
16318
|
`).filter(Boolean);
|
|
15766
16319
|
for (const line of lines) {
|
|
15767
16320
|
const event = JSON.parse(line);
|
|
@@ -15916,7 +16469,7 @@ async function readDaemonLock(homeDir) {
|
|
|
15916
16469
|
const paths2 = resolveDaemonPaths(homeDir);
|
|
15917
16470
|
if (!await pathExists6(paths2.lockPath))
|
|
15918
16471
|
return null;
|
|
15919
|
-
const lock = JSON.parse(await
|
|
16472
|
+
const lock = JSON.parse(await readFile19(paths2.lockPath, "utf8"));
|
|
15920
16473
|
if (lock.version !== 1 || lock.component !== "evodev-daemon")
|
|
15921
16474
|
throw new Error("Invalid daemon lock.");
|
|
15922
16475
|
return lock;
|
|
@@ -15925,7 +16478,7 @@ async function readDaemonToken(homeDir) {
|
|
|
15925
16478
|
const paths2 = resolveDaemonPaths(homeDir);
|
|
15926
16479
|
if (!await pathExists6(paths2.tokenPath))
|
|
15927
16480
|
return null;
|
|
15928
|
-
return (await
|
|
16481
|
+
return (await readFile19(paths2.tokenPath, "utf8")).trim();
|
|
15929
16482
|
}
|
|
15930
16483
|
async function cleanupDaemonState(homeDir, token) {
|
|
15931
16484
|
const paths2 = resolveDaemonPaths(homeDir);
|
|
@@ -16131,7 +16684,7 @@ async function collectLearningCandidateSummaries(homeDir, warnings) {
|
|
|
16131
16684
|
warnings.push("Learning candidate store not found; returning empty candidates.");
|
|
16132
16685
|
return [];
|
|
16133
16686
|
}
|
|
16134
|
-
const lines = (await
|
|
16687
|
+
const lines = (await readFile19(path, "utf8")).split(`
|
|
16135
16688
|
`).filter(Boolean);
|
|
16136
16689
|
return lines.map((line) => {
|
|
16137
16690
|
const candidate = JSON.parse(line);
|
|
@@ -16438,7 +16991,7 @@ async function readDaemonEvolutionProcessError(homeDir) {
|
|
|
16438
16991
|
const path = resolveDaemonEvolutionProcessErrorPath(homeDir);
|
|
16439
16992
|
if (!await pathExists6(path))
|
|
16440
16993
|
return null;
|
|
16441
|
-
const value = JSON.parse(await
|
|
16994
|
+
const value = JSON.parse(await readFile19(path, "utf8"));
|
|
16442
16995
|
return typeof value.summary === "string" && value.summary.length > 0 ? value.summary : null;
|
|
16443
16996
|
}
|
|
16444
16997
|
function describeError3(error) {
|
|
@@ -16886,7 +17439,7 @@ function assertDescendant(rootDir, candidate) {
|
|
|
16886
17439
|
}
|
|
16887
17440
|
// packages/core/src/evolution/imports/storage.ts
|
|
16888
17441
|
import { randomUUID as randomUUID4 } from "node:crypto";
|
|
16889
|
-
import { chmod as chmod2, link as link2, lstat as lstat3, mkdir as mkdir14, open as open3, readFile as
|
|
17442
|
+
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";
|
|
16890
17443
|
import { dirname as dirname15, join as join22 } from "node:path";
|
|
16891
17444
|
var PRIVATE_DIRECTORY_MODE = 448;
|
|
16892
17445
|
var PRIVATE_FILE_MODE = 384;
|
|
@@ -17066,7 +17619,7 @@ async function writeTrajectoryImportManifest(paths2, manifest) {
|
|
|
17066
17619
|
}
|
|
17067
17620
|
async function readTrajectoryImportIndex(path) {
|
|
17068
17621
|
await assertRegularStateFile(path);
|
|
17069
|
-
const content = await
|
|
17622
|
+
const content = await readFile20(path, "utf8");
|
|
17070
17623
|
const lines = content.endsWith(`
|
|
17071
17624
|
`) ? content.slice(0, -1).split(`
|
|
17072
17625
|
`) : content.split(`
|
|
@@ -17166,7 +17719,7 @@ async function heartbeatTrajectoryImportSourceLock(lock) {
|
|
|
17166
17719
|
}
|
|
17167
17720
|
async function trajectoryImportSourceLockIsOwned(lock) {
|
|
17168
17721
|
try {
|
|
17169
|
-
const value = JSON.parse(await
|
|
17722
|
+
const value = JSON.parse(await readFile20(lock.ownerPath, "utf8"));
|
|
17170
17723
|
return value.ownerToken === lock.ownerToken;
|
|
17171
17724
|
} catch {
|
|
17172
17725
|
return false;
|
|
@@ -17250,7 +17803,7 @@ async function restoreQuarantinedLock(quarantinePath, targetPath) {
|
|
|
17250
17803
|
}
|
|
17251
17804
|
async function removeOwnedControlFile(path, ownerToken) {
|
|
17252
17805
|
try {
|
|
17253
|
-
const value = JSON.parse(await
|
|
17806
|
+
const value = JSON.parse(await readFile20(path, "utf8"));
|
|
17254
17807
|
if (value.ownerToken === ownerToken)
|
|
17255
17808
|
await rm7(path, { force: true });
|
|
17256
17809
|
} catch {}
|
|
@@ -17277,7 +17830,7 @@ async function writePrivateImmutableFile(path, content) {
|
|
|
17277
17830
|
if (!hasErrorCode(error, "EEXIST"))
|
|
17278
17831
|
throw error;
|
|
17279
17832
|
await assertRegularStateFile(path);
|
|
17280
|
-
const existing = await
|
|
17833
|
+
const existing = await readFile20(path, "utf8");
|
|
17281
17834
|
if (existing !== content) {
|
|
17282
17835
|
throw new Error("Trajectory import immutable state digest conflict.");
|
|
17283
17836
|
}
|
|
@@ -17332,7 +17885,7 @@ async function createPrivateExclusiveFile(path, content) {
|
|
|
17332
17885
|
async function readOptionalJson(path, parser) {
|
|
17333
17886
|
try {
|
|
17334
17887
|
await assertRegularStateFile(path);
|
|
17335
|
-
const content = await
|
|
17888
|
+
const content = await readFile20(path, "utf8");
|
|
17336
17889
|
return parser(JSON.parse(content));
|
|
17337
17890
|
} catch (error) {
|
|
17338
17891
|
if (hasErrorCode(error, "ENOENT"))
|
|
@@ -17940,8 +18493,8 @@ function createHistoricalSessionEvidenceSegments(input) {
|
|
|
17940
18493
|
throw new Error("Historical materialization requires a projectKey.");
|
|
17941
18494
|
}
|
|
17942
18495
|
const createdAt = normalizeIsoTimestamp(input.createdAt);
|
|
17943
|
-
const expiresAt =
|
|
17944
|
-
const
|
|
18496
|
+
const expiresAt = addDays3(createdAt, input.plan.policy.retentionDays);
|
|
18497
|
+
const sessionKey2 = `historical-${input.plan.preview.sourceKey}`;
|
|
17945
18498
|
const target = sourceTarget(input.plan.preview.source);
|
|
17946
18499
|
return input.plan.preparedSegments.map((prepared) => ({
|
|
17947
18500
|
schemaVersion: 1,
|
|
@@ -17950,7 +18503,7 @@ function createHistoricalSessionEvidenceSegments(input) {
|
|
|
17950
18503
|
projectKey,
|
|
17951
18504
|
runId: null,
|
|
17952
18505
|
roleId: input.plan.preview.scope.roleId,
|
|
17953
|
-
sessionKey,
|
|
18506
|
+
sessionKey: sessionKey2,
|
|
17954
18507
|
target,
|
|
17955
18508
|
createdAt,
|
|
17956
18509
|
reason: "historical-import",
|
|
@@ -18348,7 +18901,7 @@ function normalizeIsoTimestamp(value) {
|
|
|
18348
18901
|
}
|
|
18349
18902
|
return new Date(value).toISOString();
|
|
18350
18903
|
}
|
|
18351
|
-
function
|
|
18904
|
+
function addDays3(value, days) {
|
|
18352
18905
|
return new Date(Date.parse(value) + days * 24 * 60 * 60 * 1000).toISOString();
|
|
18353
18906
|
}
|
|
18354
18907
|
// packages/core/src/evolution/imports/apply.ts
|
|
@@ -18395,13 +18948,13 @@ async function applyTrajectoryImport(input) {
|
|
|
18395
18948
|
plan: input.plan,
|
|
18396
18949
|
createdAt: staged.receipt.createdAt
|
|
18397
18950
|
});
|
|
18398
|
-
const
|
|
18951
|
+
const sessionKey2 = `historical-${input.plan.preview.sourceKey}`;
|
|
18399
18952
|
const sessionPaths = resolveSessionMemoryPaths({
|
|
18400
18953
|
homeDir: input.homeDir,
|
|
18401
18954
|
projectKey,
|
|
18402
|
-
sessionKey
|
|
18955
|
+
sessionKey: sessionKey2
|
|
18403
18956
|
});
|
|
18404
|
-
const expectedTriggerIds = segments.map((segment) => createStableId("segment-trigger", [projectKey,
|
|
18957
|
+
const expectedTriggerIds = segments.map((segment) => createStableId("segment-trigger", [projectKey, sessionKey2, segment.id]));
|
|
18405
18958
|
let progress = await resolveMaterializationProgress({
|
|
18406
18959
|
path: paths2.materializationPath,
|
|
18407
18960
|
previewId: input.plan.preview.id,
|
|
@@ -18434,7 +18987,7 @@ async function applyTrajectoryImport(input) {
|
|
|
18434
18987
|
homeDir: input.homeDir,
|
|
18435
18988
|
paths: sessionPaths,
|
|
18436
18989
|
projectKey,
|
|
18437
|
-
sessionKey,
|
|
18990
|
+
sessionKey: sessionKey2,
|
|
18438
18991
|
target: segments[0]?.target ?? "unknown",
|
|
18439
18992
|
roleId: input.plan.preview.scope.roleId,
|
|
18440
18993
|
policy: input.plan.policy,
|
|
@@ -18447,7 +19000,7 @@ async function applyTrajectoryImport(input) {
|
|
|
18447
19000
|
const trigger = await enqueueSegmentEvolutionTrigger({
|
|
18448
19001
|
homeDir: input.homeDir,
|
|
18449
19002
|
projectKey,
|
|
18450
|
-
sessionKey,
|
|
19003
|
+
sessionKey: sessionKey2,
|
|
18451
19004
|
runId: null,
|
|
18452
19005
|
roleId: input.plan.preview.scope.roleId,
|
|
18453
19006
|
segmentId: segment.id,
|
|
@@ -18472,7 +19025,7 @@ async function applyTrajectoryImport(input) {
|
|
|
18472
19025
|
await verifyMaterializedSegments({
|
|
18473
19026
|
homeDir: input.homeDir,
|
|
18474
19027
|
projectKey,
|
|
18475
|
-
sessionKey,
|
|
19028
|
+
sessionKey: sessionKey2,
|
|
18476
19029
|
segmentIds: progress.expectedSegmentIds
|
|
18477
19030
|
});
|
|
18478
19031
|
if (progress.completedSegmentIds.length !== progress.expectedSegmentIds.length || progress.completedTriggerIds.length !== progress.expectedTriggerIds.length) {
|
|
@@ -18545,17 +19098,17 @@ async function readCompletedApplyResult(input, manifest) {
|
|
|
18545
19098
|
if (progress.completedSegmentIds.length !== progress.expectedSegmentIds.length || progress.completedTriggerIds.length !== progress.expectedTriggerIds.length) {
|
|
18546
19099
|
throw new Error("Completed trajectory import has incomplete materialization progress.");
|
|
18547
19100
|
}
|
|
18548
|
-
const
|
|
19101
|
+
const sessionKey2 = `historical-${manifest.sourceKey}`;
|
|
18549
19102
|
await verifyMaterializedSegments({
|
|
18550
19103
|
homeDir: input.homeDir,
|
|
18551
19104
|
projectKey,
|
|
18552
|
-
sessionKey,
|
|
19105
|
+
sessionKey: sessionKey2,
|
|
18553
19106
|
segmentIds: progress.expectedSegmentIds
|
|
18554
19107
|
});
|
|
18555
19108
|
const triggerIds = new Set((await listSegmentEvolutionTriggers({
|
|
18556
19109
|
homeDir: input.homeDir,
|
|
18557
19110
|
projectKey
|
|
18558
|
-
})).filter((trigger) => trigger.sessionKey ===
|
|
19111
|
+
})).filter((trigger) => trigger.sessionKey === sessionKey2 && progress.expectedSegmentIds.includes(trigger.segmentId)).map((trigger) => trigger.id));
|
|
18559
19112
|
if (progress.expectedTriggerIds.some((id) => !triggerIds.has(id))) {
|
|
18560
19113
|
throw new Error("Completed trajectory import is missing a materialized trigger.");
|
|
18561
19114
|
}
|
|
@@ -18931,7 +19484,7 @@ function isBaseRevisionConflict(error) {
|
|
|
18931
19484
|
return typeof error === "object" && error !== null && "code" in error && error.code === "KNOWLEDGE_BASE_REVISION_CONFLICT";
|
|
18932
19485
|
}
|
|
18933
19486
|
// packages/core/src/evolution/review/index.ts
|
|
18934
|
-
import { mkdir as mkdir15, readFile as
|
|
19487
|
+
import { mkdir as mkdir15, readFile as readFile21, stat as stat11, writeFile as writeFile12 } from "node:fs/promises";
|
|
18935
19488
|
import { dirname as dirname16, join as join23 } from "node:path";
|
|
18936
19489
|
var LEARNING_CANDIDATE_KINDS = [
|
|
18937
19490
|
"lesson",
|
|
@@ -19282,7 +19835,7 @@ function requiredFields(candidate) {
|
|
|
19282
19835
|
];
|
|
19283
19836
|
}
|
|
19284
19837
|
async function parseJsonOrJsonlFile(path, parseItem) {
|
|
19285
|
-
const raw = await
|
|
19838
|
+
const raw = await readFile21(path, "utf8");
|
|
19286
19839
|
if (raw.trim() === "")
|
|
19287
19840
|
return [];
|
|
19288
19841
|
if (path.endsWith(".jsonl")) {
|
|
@@ -19360,7 +19913,7 @@ function isRecord11(value) {
|
|
|
19360
19913
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
19361
19914
|
}
|
|
19362
19915
|
// packages/core/src/pack/index.ts
|
|
19363
|
-
import { readFile as
|
|
19916
|
+
import { readFile as readFile22, readdir as readdir14, stat as stat12 } from "node:fs/promises";
|
|
19364
19917
|
import { isAbsolute as isAbsolute8, join as join24, relative as relative8, sep as sep2 } from "node:path";
|
|
19365
19918
|
|
|
19366
19919
|
// packages/core/src/protected-zones/index.ts
|
|
@@ -19511,7 +20064,7 @@ async function validatePack(packPath) {
|
|
|
19511
20064
|
let assets = [];
|
|
19512
20065
|
let scannedPaths = [];
|
|
19513
20066
|
try {
|
|
19514
|
-
manifest = parsePackManifest(JSON.parse(await
|
|
20067
|
+
manifest = parsePackManifest(JSON.parse(await readFile22(manifestPath, "utf8")));
|
|
19515
20068
|
} catch (error) {
|
|
19516
20069
|
findings.push({
|
|
19517
20070
|
severity: "error",
|
|
@@ -20042,7 +20595,7 @@ function formatError(error) {
|
|
|
20042
20595
|
return error instanceof Error ? error.message : String(error);
|
|
20043
20596
|
}
|
|
20044
20597
|
// packages/core/src/plugins/capabilities.ts
|
|
20045
|
-
import { mkdir as mkdir16, readFile as
|
|
20598
|
+
import { mkdir as mkdir16, readFile as readFile23, writeFile as writeFile13 } from "node:fs/promises";
|
|
20046
20599
|
import { dirname as dirname17, join as join25 } from "node:path";
|
|
20047
20600
|
async function createCodexCapabilityVerificationArtifact(input) {
|
|
20048
20601
|
const timestamp = input.createdAt ?? new Date().toISOString();
|
|
@@ -20712,7 +21265,7 @@ function describeError4(error) {
|
|
|
20712
21265
|
return error instanceof Error ? error.message : String(error);
|
|
20713
21266
|
}
|
|
20714
21267
|
// packages/core/src/workflow/index.ts
|
|
20715
|
-
import { readFile as
|
|
21268
|
+
import { readFile as readFile24, readdir as readdir15 } from "node:fs/promises";
|
|
20716
21269
|
import { join as join27 } from "node:path";
|
|
20717
21270
|
async function scanWorkflowManifests(workflowsDir) {
|
|
20718
21271
|
const manifests = [];
|
|
@@ -20721,7 +21274,7 @@ async function scanWorkflowManifests(workflowsDir) {
|
|
|
20721
21274
|
if (!entry.isDirectory())
|
|
20722
21275
|
continue;
|
|
20723
21276
|
const manifestPath = join27(workflowsDir, entry.name, "WORKFLOW.json");
|
|
20724
|
-
manifests.push(parseWorkflowManifest(JSON.parse(await
|
|
21277
|
+
manifests.push(parseWorkflowManifest(JSON.parse(await readFile24(manifestPath, "utf8"))));
|
|
20725
21278
|
}
|
|
20726
21279
|
return manifests.sort((left, right) => left.id.localeCompare(right.id));
|
|
20727
21280
|
}
|
|
@@ -25247,7 +25800,7 @@ var dist_default5 = createPrompt((config2, done) => {
|
|
|
25247
25800
|
return `${lines}${cursorHide}`;
|
|
25248
25801
|
});
|
|
25249
25802
|
// packages/plugin/hooks/codex.ts
|
|
25250
|
-
import { mkdir as mkdir20, readFile as
|
|
25803
|
+
import { mkdir as mkdir20, readFile as readFile27, readdir as readdir18, stat as stat16, writeFile as writeFile18 } from "node:fs/promises";
|
|
25251
25804
|
import { dirname as dirname23 } from "node:path";
|
|
25252
25805
|
|
|
25253
25806
|
// packages/plugin/hooks/command-runner.ts
|
|
@@ -25275,7 +25828,7 @@ async function runNodeCommand(command, args) {
|
|
|
25275
25828
|
}
|
|
25276
25829
|
|
|
25277
25830
|
// packages/plugin/hooks/hooks.ts
|
|
25278
|
-
import { copyFile, mkdir as mkdir18, readFile as
|
|
25831
|
+
import { copyFile, mkdir as mkdir18, readFile as readFile25, readdir as readdir16, stat as stat14, writeFile as writeFile16 } from "node:fs/promises";
|
|
25279
25832
|
import { dirname as dirname21, join as join30 } from "node:path";
|
|
25280
25833
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
25281
25834
|
|
|
@@ -25597,7 +26150,7 @@ function createClaudeHookConfig(command = CLAUDE_HOOK_RUNTIME_COMMAND) {
|
|
|
25597
26150
|
}
|
|
25598
26151
|
async function readTextIfExists(path2) {
|
|
25599
26152
|
try {
|
|
25600
|
-
return await
|
|
26153
|
+
return await readFile25(path2, "utf8");
|
|
25601
26154
|
} catch (error) {
|
|
25602
26155
|
if (isNotFoundError8(error))
|
|
25603
26156
|
return null;
|
|
@@ -25822,7 +26375,7 @@ function tomlString(value) {
|
|
|
25822
26375
|
}
|
|
25823
26376
|
|
|
25824
26377
|
// packages/plugin/hooks/workspace-core.ts
|
|
25825
|
-
import { cp as cp2, mkdir as mkdir19, readFile as
|
|
26378
|
+
import { cp as cp2, mkdir as mkdir19, readFile as readFile26, readdir as readdir17, rm as rm9, stat as stat15, writeFile as writeFile17 } from "node:fs/promises";
|
|
25826
26379
|
import { dirname as dirname22 } from "node:path";
|
|
25827
26380
|
async function hydrateWorkspaceCoreDependency(input) {
|
|
25828
26381
|
const warnings = [];
|
|
@@ -25889,7 +26442,7 @@ async function writeRuntimeCorePackage(input) {
|
|
|
25889
26442
|
recursive: true
|
|
25890
26443
|
});
|
|
25891
26444
|
}
|
|
25892
|
-
const sourcePackageJson = JSON.parse(await
|
|
26445
|
+
const sourcePackageJson = JSON.parse(await readFile26(sourcePackagePath, "utf8"));
|
|
25893
26446
|
const runtimePackageJson = {
|
|
25894
26447
|
...sourcePackageJson,
|
|
25895
26448
|
files: ["src", "assets", "package.json"]
|
|
@@ -25943,7 +26496,7 @@ var CODEX_CAPABILITY_HOOK_EVENTS = [
|
|
|
25943
26496
|
];
|
|
25944
26497
|
var nodeSyncFileSystem = {
|
|
25945
26498
|
async readFile(path2) {
|
|
25946
|
-
return
|
|
26499
|
+
return readFile27(path2, "utf8");
|
|
25947
26500
|
},
|
|
25948
26501
|
async writeFile(path2, content, options = {}) {
|
|
25949
26502
|
await writeFile18(path2, content, {
|
|
@@ -26380,7 +26933,7 @@ function summarizeHookCommand(command) {
|
|
|
26380
26933
|
}
|
|
26381
26934
|
async function readTextFileIfExists(path2) {
|
|
26382
26935
|
try {
|
|
26383
|
-
return await
|
|
26936
|
+
return await readFile27(path2, "utf8");
|
|
26384
26937
|
} catch (error) {
|
|
26385
26938
|
if (isNotFoundError10(error))
|
|
26386
26939
|
return null;
|
|
@@ -26581,7 +27134,7 @@ function describeError7(error) {
|
|
|
26581
27134
|
}
|
|
26582
27135
|
// packages/plugin/hooks/plugin.ts
|
|
26583
27136
|
import { constants } from "node:fs";
|
|
26584
|
-
import { access, mkdir as mkdir21, readFile as
|
|
27137
|
+
import { access, mkdir as mkdir21, readFile as readFile28, stat as stat17, writeFile as writeFile19 } from "node:fs/promises";
|
|
26585
27138
|
import { dirname as dirname24 } from "node:path";
|
|
26586
27139
|
|
|
26587
27140
|
// packages/plugin/hooks/transform-skill.ts
|
|
@@ -26661,7 +27214,7 @@ function createClaudePlugin(options = {}) {
|
|
|
26661
27214
|
}
|
|
26662
27215
|
var nodeSyncFileSystem2 = {
|
|
26663
27216
|
async readFile(path2) {
|
|
26664
|
-
return
|
|
27217
|
+
return readFile28(path2, "utf8");
|
|
26665
27218
|
},
|
|
26666
27219
|
async writeFile(path2, content, options = {}) {
|
|
26667
27220
|
await writeFile19(path2, content, {
|
|
@@ -27256,7 +27809,7 @@ function defaultSleep(ms) {
|
|
|
27256
27809
|
}
|
|
27257
27810
|
|
|
27258
27811
|
// packages/cli/src/version.ts
|
|
27259
|
-
var CLI_VERSION = "0.0.1-alpha.
|
|
27812
|
+
var CLI_VERSION = "0.0.1-alpha.20";
|
|
27260
27813
|
|
|
27261
27814
|
// packages/cli/src/init.ts
|
|
27262
27815
|
var DEFAULT_CODE_AGENT_MARKETPLACE_SOURCE = "limerickgds/evodev";
|
|
@@ -28292,29 +28845,29 @@ async function runDoctor(options = {}) {
|
|
|
28292
28845
|
}
|
|
28293
28846
|
async function runHistoricalEvidenceRetentionCheck(homeDir) {
|
|
28294
28847
|
try {
|
|
28295
|
-
const retention2 = await
|
|
28296
|
-
if (retention2.overdue === 0) {
|
|
28848
|
+
const retention2 = await inspectSessionEvidenceRetention({ homeDir });
|
|
28849
|
+
if (retention2.overdue === 0 && retention2.legacyOverdue === 0) {
|
|
28297
28850
|
return {
|
|
28298
28851
|
id: "evodev.historical-evidence-retention",
|
|
28299
28852
|
group: "EvoDev",
|
|
28300
|
-
label: "
|
|
28853
|
+
label: "session evidence retention",
|
|
28301
28854
|
status: "pass",
|
|
28302
|
-
message: `${retention2.available} raw segment(s) available; none overdue`
|
|
28855
|
+
message: `${retention2.available} managed raw segment(s) available; ${retention2.legacyWithoutRetention} legacy segment(s); none overdue`
|
|
28303
28856
|
};
|
|
28304
28857
|
}
|
|
28305
28858
|
return {
|
|
28306
28859
|
id: "evodev.historical-evidence-retention",
|
|
28307
28860
|
group: "EvoDev",
|
|
28308
|
-
label: "
|
|
28861
|
+
label: "session evidence retention",
|
|
28309
28862
|
status: "warn",
|
|
28310
|
-
message: `${retention2.overdue} raw segment(s) overdue (${retention2.purgeEligible} purge eligible, ${retention2.protected} protected by pending processing or review)`,
|
|
28311
|
-
fix: retention2.purgeEligible > 0 ? "
|
|
28863
|
+
message: `${retention2.overdue + retention2.legacyOverdue} raw segment(s) overdue (${retention2.purgeEligible} purge eligible, ${retention2.protected} protected by pending processing or review)`,
|
|
28864
|
+
fix: retention2.purgeEligible > 0 ? "Preview with evodev evo retention, then apply legacy retention explicitly when ready." : "Finish or explicitly reject/defer pending Session Evidence processing and review."
|
|
28312
28865
|
};
|
|
28313
28866
|
} catch (error) {
|
|
28314
28867
|
return {
|
|
28315
28868
|
id: "evodev.historical-evidence-retention",
|
|
28316
28869
|
group: "EvoDev",
|
|
28317
|
-
label: "
|
|
28870
|
+
label: "session evidence retention",
|
|
28318
28871
|
status: "warn",
|
|
28319
28872
|
message: describeError9(error),
|
|
28320
28873
|
fix: "Inspect local Session Evidence and evolution trigger state."
|
|
@@ -28846,14 +29399,14 @@ function throwReviewWriteError(error) {
|
|
|
28846
29399
|
// packages/cli/src/session-knowledge.ts
|
|
28847
29400
|
import { spawn as spawn3 } from "node:child_process";
|
|
28848
29401
|
import { createHash as createHash8 } from "node:crypto";
|
|
28849
|
-
import { mkdtemp, readFile as
|
|
29402
|
+
import { mkdtemp, readFile as readFile30, realpath as realpath4, rm as rm10, stat as stat21, writeFile as writeFile20 } from "node:fs/promises";
|
|
28850
29403
|
import { tmpdir as tmpdir2 } from "node:os";
|
|
28851
29404
|
import { dirname as dirname25, join as join32, resolve as resolve11 } from "node:path";
|
|
28852
29405
|
|
|
28853
29406
|
// packages/cli/src/ui/session-evidence.ts
|
|
28854
29407
|
import { createHash as createHash7 } from "node:crypto";
|
|
28855
29408
|
import { createReadStream } from "node:fs";
|
|
28856
|
-
import { readFile as
|
|
29409
|
+
import { readFile as readFile29, realpath as realpath3, stat as stat20 } from "node:fs/promises";
|
|
28857
29410
|
import { isAbsolute as isAbsolute10, relative as relative11 } from "node:path";
|
|
28858
29411
|
import { createInterface as createInterface3 } from "node:readline";
|
|
28859
29412
|
var MAX_TRANSCRIPT_BYTES = 32 * 1024 * 1024;
|
|
@@ -29111,7 +29664,7 @@ async function readTranscriptRecords(path2, target, pointer) {
|
|
|
29111
29664
|
if (!info.isFile())
|
|
29112
29665
|
throw new Error("Native transcript is not a regular file.");
|
|
29113
29666
|
if (info.size <= MAX_TRANSCRIPT_BYTES) {
|
|
29114
|
-
return { records: parseJsonLines(await
|
|
29667
|
+
return { records: parseJsonLines(await readFile29(path2, "utf8")), truncated: false };
|
|
29115
29668
|
}
|
|
29116
29669
|
return {
|
|
29117
29670
|
records: await readLargeTranscriptWindow(path2, target, pointer),
|
|
@@ -29562,7 +30115,7 @@ function createCodexSessionKnowledgeAnalyzer(input = {}) {
|
|
|
29562
30115
|
prompt: redactSessionMemoryCredentialText(createAnalyzerPrompt(evidence)).value,
|
|
29563
30116
|
timeoutMs: input.timeoutMs ?? MODEL_TIMEOUT_MS
|
|
29564
30117
|
});
|
|
29565
|
-
return parseSemanticKnowledgeAnalysis(JSON.parse(await
|
|
30118
|
+
return parseSemanticKnowledgeAnalysis(JSON.parse(await readFile30(outputPath, "utf8")));
|
|
29566
30119
|
} finally {
|
|
29567
30120
|
await rm10(workDir, { recursive: true, force: true });
|
|
29568
30121
|
}
|
|
@@ -29632,7 +30185,8 @@ function buildKnowledgeDistillationOutput(input, analysis3) {
|
|
|
29632
30185
|
const category = categoryForKind(candidate.kind);
|
|
29633
30186
|
const targetPath = `concepts/${category}/${slugify(candidate.title)}.md`;
|
|
29634
30187
|
const roleTags = uniqueSlugs(candidate.roleTags.length > 0 ? candidate.roleTags : [input.segment.roleId ?? input.segment.target]);
|
|
29635
|
-
const
|
|
30188
|
+
const supportRef = createCandidateSupportRef(input, candidate, sourceId);
|
|
30189
|
+
const requiresHumanReview = candidate.operation === "revoke" || candidate.requiresHumanReview || candidate.basis === "inferred" || supportRef.kind === "semantic-inference" || /security|privacy|release|architecture|cross[-_ ]?repo/i.test(`${candidate.kind} ${candidate.title} ${candidate.claim}`);
|
|
29636
30190
|
const decision = candidate.operation === "revoke" ? "revoke" : requiresHumanReview ? "needs-human" : "create";
|
|
29637
30191
|
const reviewState = requiresHumanReview ? "needs-human" : "accepted";
|
|
29638
30192
|
const evalId = `eval-${id}`;
|
|
@@ -29668,6 +30222,7 @@ function buildKnowledgeDistillationOutput(input, analysis3) {
|
|
|
29668
30222
|
},
|
|
29669
30223
|
decisionReason: candidate.operation === "revoke" ? "Verified evidence proposes revoking existing knowledge; human review is required." : requiresHumanReview ? "Requires human review before activation." : "Curator candidate pending local evidence, quality, privacy, scope, and duplicate gates.",
|
|
29670
30224
|
evidenceRefs: [sourceId],
|
|
30225
|
+
supportRef,
|
|
29671
30226
|
reviewState,
|
|
29672
30227
|
...candidate.verification.length === 0 && candidate.verificationNotApplicableReason.trim() !== "" ? { verificationNotApplicableReason: candidate.verificationNotApplicableReason } : {},
|
|
29673
30228
|
evalSetRefs: [evalId],
|
|
@@ -29734,6 +30289,18 @@ function buildKnowledgeDistillationOutput(input, analysis3) {
|
|
|
29734
30289
|
privacyCheck
|
|
29735
30290
|
};
|
|
29736
30291
|
}
|
|
30292
|
+
function createCandidateSupportRef(input, candidate, sourceRefId) {
|
|
30293
|
+
const userDerived = candidate.basis === "direct" && (input.segment.reason === "explicit-memory-intent" || input.segment.reason === "user-interruption" || input.segment.reason === "intent-refinement");
|
|
30294
|
+
const verifiedOutcome = candidate.basis === "direct" && input.segment.reason === "verification-after-fix" && input.segment.normalized.verifications.length > 0;
|
|
30295
|
+
const kind = verifiedOutcome ? "verified-outcome" : userDerived ? "user-declaration" : "semantic-inference";
|
|
30296
|
+
return {
|
|
30297
|
+
id: createLocalStableId("support", [input.segment.id, sourceRefId, kind]),
|
|
30298
|
+
kind,
|
|
30299
|
+
sourceRefId,
|
|
30300
|
+
subjectFingerprint: input.segment.rawExcerpt.sha256,
|
|
30301
|
+
observedAt: input.segment.createdAt
|
|
30302
|
+
};
|
|
30303
|
+
}
|
|
29737
30304
|
function createNoWritePlan(input, reason) {
|
|
29738
30305
|
return finalizeCuratedKnowledgePlan({
|
|
29739
30306
|
homeDir: input.homeDir,
|
|
@@ -32865,8 +33432,97 @@ async function runEvoCommand(argv, options = {}) {
|
|
|
32865
33432
|
write
|
|
32866
33433
|
});
|
|
32867
33434
|
}
|
|
33435
|
+
if (subcommand === "retention") {
|
|
33436
|
+
const flags = parseEvoRetentionFlags(argv.slice(1));
|
|
33437
|
+
if (flags.apply) {
|
|
33438
|
+
const result = await migrateLegacySessionEvidenceRetention({
|
|
33439
|
+
homeDir,
|
|
33440
|
+
projectKey: flags.projectKey,
|
|
33441
|
+
now: resolveNow2(options)
|
|
33442
|
+
});
|
|
33443
|
+
write(formatRetentionMigration(result));
|
|
33444
|
+
return 0;
|
|
33445
|
+
}
|
|
33446
|
+
const inspection = await inspectSessionEvidenceRetention({
|
|
33447
|
+
homeDir,
|
|
33448
|
+
projectKey: flags.projectKey,
|
|
33449
|
+
now: resolveNow2(options)
|
|
33450
|
+
});
|
|
33451
|
+
write(formatRetentionPreview(inspection));
|
|
33452
|
+
return 0;
|
|
33453
|
+
}
|
|
32868
33454
|
throw new Error(`Unknown evo command: ${subcommand ?? ""}`.trim());
|
|
32869
33455
|
}
|
|
33456
|
+
function parseEvoRetentionFlags(argv) {
|
|
33457
|
+
let projectKey;
|
|
33458
|
+
let apply2 = false;
|
|
33459
|
+
let dryRun = false;
|
|
33460
|
+
let yes = false;
|
|
33461
|
+
for (let index = 0;index < argv.length; index += 1) {
|
|
33462
|
+
const arg = argv[index];
|
|
33463
|
+
if (arg === "--project") {
|
|
33464
|
+
const value = argv[index + 1];
|
|
33465
|
+
if (value === undefined || value.startsWith("--")) {
|
|
33466
|
+
throw new Error("Missing value for --project");
|
|
33467
|
+
}
|
|
33468
|
+
projectKey = value;
|
|
33469
|
+
index += 1;
|
|
33470
|
+
continue;
|
|
33471
|
+
}
|
|
33472
|
+
if (arg === "--apply") {
|
|
33473
|
+
apply2 = true;
|
|
33474
|
+
continue;
|
|
33475
|
+
}
|
|
33476
|
+
if (arg === "--dry-run") {
|
|
33477
|
+
dryRun = true;
|
|
33478
|
+
continue;
|
|
33479
|
+
}
|
|
33480
|
+
if (arg === "--yes") {
|
|
33481
|
+
yes = true;
|
|
33482
|
+
continue;
|
|
33483
|
+
}
|
|
33484
|
+
throw new Error(`Unknown evo retention option: ${arg}`);
|
|
33485
|
+
}
|
|
33486
|
+
if (apply2 && dryRun)
|
|
33487
|
+
throw new Error("Use only one of --dry-run or --apply");
|
|
33488
|
+
if (apply2 && !yes)
|
|
33489
|
+
throw new Error("evo retention --apply requires --yes");
|
|
33490
|
+
if (!apply2 && yes)
|
|
33491
|
+
throw new Error("evo retention --yes requires --apply");
|
|
33492
|
+
return { projectKey, apply: apply2 };
|
|
33493
|
+
}
|
|
33494
|
+
function formatRetentionPreview(result) {
|
|
33495
|
+
return [
|
|
33496
|
+
"EvoDev Session Evidence retention preview",
|
|
33497
|
+
"",
|
|
33498
|
+
`Managed available: ${result.available}`,
|
|
33499
|
+
`Managed overdue: ${result.overdue}`,
|
|
33500
|
+
`Legacy without retention: ${result.legacyWithoutRetention}`,
|
|
33501
|
+
`Legacy overdue: ${result.legacyOverdue}`,
|
|
33502
|
+
`Legacy raw bytes: ${result.legacyRawBytes}`,
|
|
33503
|
+
`Legacy overdue raw bytes: ${result.legacyOverdueRawBytes}`,
|
|
33504
|
+
`Expired captured event payloads: ${result.expiredCapturedEventPayloads}`,
|
|
33505
|
+
`Expired captured event raw bytes: ${result.expiredCapturedEventRawBytes}`,
|
|
33506
|
+
`Purge eligible: ${result.purgeEligible}`,
|
|
33507
|
+
`Protected: ${result.protected}`,
|
|
33508
|
+
"No files changed.",
|
|
33509
|
+
"Apply with: evodev evo retention --apply --yes"
|
|
33510
|
+
].join(`
|
|
33511
|
+
`);
|
|
33512
|
+
}
|
|
33513
|
+
function formatRetentionMigration(result) {
|
|
33514
|
+
return [
|
|
33515
|
+
"EvoDev Session Evidence retention migration completed",
|
|
33516
|
+
"",
|
|
33517
|
+
`Eligible legacy segments migrated: ${result.migrated}`,
|
|
33518
|
+
`Captured event payloads expired: ${result.expiredCapturedEventPayloads}`,
|
|
33519
|
+
`Captured event raw bytes removed: ${result.expiredCapturedEventRawBytes}`,
|
|
33520
|
+
`Due segments scanned: ${result.purge.due}`,
|
|
33521
|
+
`Raw segments purged: ${result.purge.purged}`,
|
|
33522
|
+
`Protected: ${result.purge.protected.length}`
|
|
33523
|
+
].join(`
|
|
33524
|
+
`);
|
|
33525
|
+
}
|
|
32870
33526
|
async function runEvoReviewDecision(argv, input) {
|
|
32871
33527
|
const flags = parseEvoReviewDecisionFlags(argv);
|
|
32872
33528
|
if (!flags.yes && !flags.dryRun) {
|
|
@@ -33300,7 +33956,7 @@ function resolveNow2(options) {
|
|
|
33300
33956
|
}
|
|
33301
33957
|
|
|
33302
33958
|
// packages/cli/src/hook.ts
|
|
33303
|
-
import { readFile as
|
|
33959
|
+
import { readFile as readFile31, stat as stat22 } from "node:fs/promises";
|
|
33304
33960
|
var MAX_HOOK_FIXTURE_BYTES = 64 * 1024;
|
|
33305
33961
|
async function runHookCommand(argv, options = {}) {
|
|
33306
33962
|
const subcommand = argv[0];
|
|
@@ -33337,7 +33993,7 @@ async function runHookCommand(argv, options = {}) {
|
|
|
33337
33993
|
if (payloadStat.size > MAX_HOOK_FIXTURE_BYTES) {
|
|
33338
33994
|
throw new Error(`Hook fixture payload exceeds ${MAX_HOOK_FIXTURE_BYTES} byte limit.`);
|
|
33339
33995
|
}
|
|
33340
|
-
const payload = JSON.parse(await
|
|
33996
|
+
const payload = JSON.parse(await readFile31(flags.from, "utf8"));
|
|
33341
33997
|
const event = flags.target === "codex" ? normalizeCodexHookPayload({ type: flags.type, payload, receivedAt: "dry-run" }) : normalizeClaudeHookPayload({ type: flags.type, payload, receivedAt: "dry-run" });
|
|
33342
33998
|
write(formatHookEventDryRun(event));
|
|
33343
33999
|
return event.decision.action === "warn" ? 1 : 0;
|
|
@@ -33530,7 +34186,7 @@ async function readHookSettings(homeDir) {
|
|
|
33530
34186
|
}
|
|
33531
34187
|
async function appendExecutionEventSafely(input) {
|
|
33532
34188
|
try {
|
|
33533
|
-
const
|
|
34189
|
+
const sessionKey2 = input.payload === null ? "session-local" : resolveTraceSessionKey(input.payload);
|
|
33534
34190
|
const team2 = resolveTraceTeamContext({
|
|
33535
34191
|
homeDir: input.homeDir,
|
|
33536
34192
|
environment: input.environment,
|
|
@@ -33541,7 +34197,7 @@ async function appendExecutionEventSafely(input) {
|
|
|
33541
34197
|
eventId: input.event?.eventId,
|
|
33542
34198
|
target: input.target,
|
|
33543
34199
|
eventType: input.event?.type ?? runtimePhaseEventType(input.phase),
|
|
33544
|
-
sessionKey,
|
|
34200
|
+
sessionKey: sessionKey2,
|
|
33545
34201
|
projectKey: team2?.projectKey ?? null,
|
|
33546
34202
|
runId: team2?.runId ?? null,
|
|
33547
34203
|
roleId: team2?.roleId ?? null,
|
|
@@ -34101,7 +34757,7 @@ function formatKnowledgeLifecycleMutationResult(result) {
|
|
|
34101
34757
|
}
|
|
34102
34758
|
|
|
34103
34759
|
// packages/cli/src/lazy-evolution.ts
|
|
34104
|
-
import { mkdir as mkdir22, readFile as
|
|
34760
|
+
import { mkdir as mkdir22, readFile as readFile32, writeFile as writeFile21 } from "node:fs/promises";
|
|
34105
34761
|
import { dirname as dirname26, join as join33 } from "node:path";
|
|
34106
34762
|
var DEFAULT_LAZY_EVOLUTION_LIMIT = 3;
|
|
34107
34763
|
var DEFAULT_LAZY_EVOLUTION_INTERVAL_MS = 5 * 60 * 1000;
|
|
@@ -34233,7 +34889,7 @@ function resolveNow3(options) {
|
|
|
34233
34889
|
}
|
|
34234
34890
|
async function readLazyEvolutionState(path2) {
|
|
34235
34891
|
try {
|
|
34236
|
-
return parseLazyEvolutionState(JSON.parse(await
|
|
34892
|
+
return parseLazyEvolutionState(JSON.parse(await readFile32(path2, "utf8")));
|
|
34237
34893
|
} catch (error) {
|
|
34238
34894
|
if (isMissingFileError(error))
|
|
34239
34895
|
return null;
|
|
@@ -34418,7 +35074,7 @@ function resolveHomeDir8(homeDir) {
|
|
|
34418
35074
|
var DEFAULT_POST_COMMAND_RETENTION_LIMIT = 3;
|
|
34419
35075
|
async function runPostCommandMaintenance(input) {
|
|
34420
35076
|
return {
|
|
34421
|
-
|
|
35077
|
+
sessionEvidence: await purgeExpiredSessionEvidence({
|
|
34422
35078
|
homeDir: resolveHomeDir9(input.homeDir),
|
|
34423
35079
|
now: input.now?.(),
|
|
34424
35080
|
limit: input.retentionLimit ?? DEFAULT_POST_COMMAND_RETENTION_LIMIT
|
|
@@ -34684,7 +35340,7 @@ function resolveHomeDir11(homeDir) {
|
|
|
34684
35340
|
|
|
34685
35341
|
// packages/cli/src/daily-schedule.ts
|
|
34686
35342
|
import { randomUUID as randomUUID6 } from "node:crypto";
|
|
34687
|
-
import { mkdir as mkdir23, readFile as
|
|
35343
|
+
import { mkdir as mkdir23, readFile as readFile33, rename as rename6, rm as rm11, writeFile as writeFile22 } from "node:fs/promises";
|
|
34688
35344
|
import { dirname as dirname27, join as join34 } from "node:path";
|
|
34689
35345
|
async function prepareDailyEvolutionSchedule(input) {
|
|
34690
35346
|
const now = asDate(input.now);
|
|
@@ -34750,7 +35406,7 @@ async function readDailyEvolutionScheduleStatus(input) {
|
|
|
34750
35406
|
}
|
|
34751
35407
|
async function readDailyEvolutionScheduleState(homeDir) {
|
|
34752
35408
|
try {
|
|
34753
|
-
const value = JSON.parse(await
|
|
35409
|
+
const value = JSON.parse(await readFile33(resolveDailyScheduleStatePath(homeDir), "utf8"));
|
|
34754
35410
|
if (value.schemaVersion !== 1 || value.kind !== "daily-evolution-schedule-state" || typeof value.configuredTime !== "string" || typeof value.configuredAt !== "string" || typeof value.notBefore !== "string" || !isIsoTimestamp(value.configuredAt) || !isIsoTimestamp(value.notBefore) || !isNullableLocalDay(value.lastRunDay) || !isNullableTimestamp(value.lastRunAt) || !isNullableString(value.lastJobId) || !isEvolutionJobStatus(value.lastJobStatus)) {
|
|
34755
35411
|
return null;
|
|
34756
35412
|
}
|
|
@@ -34844,12 +35500,12 @@ async function writeState(homeDir, state) {
|
|
|
34844
35500
|
|
|
34845
35501
|
// packages/cli/src/evolution-job.ts
|
|
34846
35502
|
import { randomUUID as randomUUID8 } from "node:crypto";
|
|
34847
|
-
import { mkdir as mkdir26, readFile as
|
|
35503
|
+
import { mkdir as mkdir26, readFile as readFile37, readdir as readdir21, rename as rename8, rm as rm15, stat as stat26, utimes, writeFile as writeFile26 } from "node:fs/promises";
|
|
34848
35504
|
import { dirname as dirname30, join as join38 } from "node:path";
|
|
34849
35505
|
|
|
34850
35506
|
// packages/cli/src/improvement-eval.ts
|
|
34851
35507
|
import { spawn as spawn4 } from "node:child_process";
|
|
34852
|
-
import { mkdtemp as mkdtemp2, readFile as
|
|
35508
|
+
import { mkdtemp as mkdtemp2, readFile as readFile34, realpath as realpath5, rm as rm12, stat as stat23, writeFile as writeFile23 } from "node:fs/promises";
|
|
34853
35509
|
import { tmpdir as tmpdir3 } from "node:os";
|
|
34854
35510
|
import { join as join35 } from "node:path";
|
|
34855
35511
|
var DEFAULT_EVAL_LIMIT = 1;
|
|
@@ -35018,7 +35674,7 @@ function createCodexImprovementEvalAnalyzer(input = {}) {
|
|
|
35018
35674
|
timeoutMs: input.timeoutMs ?? MODEL_TIMEOUT_MS2
|
|
35019
35675
|
});
|
|
35020
35676
|
return {
|
|
35021
|
-
...parseImprovementEvalReplayAnalysis(JSON.parse(await
|
|
35677
|
+
...parseImprovementEvalReplayAnalysis(JSON.parse(await readFile34(outputPath, "utf8"))),
|
|
35022
35678
|
metrics
|
|
35023
35679
|
};
|
|
35024
35680
|
} finally {
|
|
@@ -35339,7 +35995,7 @@ import {
|
|
|
35339
35995
|
mkdir as mkdir25,
|
|
35340
35996
|
mkdtemp as mkdtemp3,
|
|
35341
35997
|
open as open6,
|
|
35342
|
-
readFile as
|
|
35998
|
+
readFile as readFile36,
|
|
35343
35999
|
readdir as readdir20,
|
|
35344
36000
|
realpath as realpath7,
|
|
35345
36001
|
rm as rm14,
|
|
@@ -35352,7 +36008,7 @@ import { basename as basename8, dirname as dirname29, isAbsolute as isAbsolute13
|
|
|
35352
36008
|
// packages/cli/src/proposal-execution.ts
|
|
35353
36009
|
import { spawn as spawn5 } from "node:child_process";
|
|
35354
36010
|
import { createHash as createHash11, randomBytes as randomBytes3 } from "node:crypto";
|
|
35355
|
-
import { mkdir as mkdir24, readFile as
|
|
36011
|
+
import { mkdir as mkdir24, readFile as readFile35, readdir as readdir19, realpath as realpath6, rename as rename7, rm as rm13, stat as stat24, writeFile as writeFile24 } from "node:fs/promises";
|
|
35356
36012
|
import { dirname as dirname28, isAbsolute as isAbsolute12, join as join36, relative as relative13, resolve as resolve13 } from "node:path";
|
|
35357
36013
|
var MAX_COMMAND_OUTPUT_BYTES = 1024 * 1024;
|
|
35358
36014
|
var MAX_DIRTY_FILES_IN_RESPONSE = 20;
|
|
@@ -35680,7 +36336,7 @@ async function listRepoProposalExecutionRecords(input) {
|
|
|
35680
36336
|
const records = [];
|
|
35681
36337
|
for (const file of files) {
|
|
35682
36338
|
try {
|
|
35683
|
-
records.push(parseExecutionRecord(JSON.parse(await
|
|
36339
|
+
records.push(parseExecutionRecord(JSON.parse(await readFile35(join36(dir, file), "utf8"))));
|
|
35684
36340
|
} catch {}
|
|
35685
36341
|
}
|
|
35686
36342
|
return records.sort((left2, right2) => left2.startedAt.localeCompare(right2.startedAt));
|
|
@@ -36064,7 +36720,7 @@ async function acquireExecutionLock(path2, latest) {
|
|
|
36064
36720
|
}
|
|
36065
36721
|
async function isExecutionLockOwnerAlive(path2) {
|
|
36066
36722
|
try {
|
|
36067
|
-
const value = JSON.parse(await
|
|
36723
|
+
const value = JSON.parse(await readFile35(path2, "utf8"));
|
|
36068
36724
|
return isRecord21(value) && typeof value.pid === "number" && isProcessAlive2(value.pid);
|
|
36069
36725
|
} catch {
|
|
36070
36726
|
return false;
|
|
@@ -36531,7 +37187,7 @@ function createCodexSessionProposalAnalyzer(input = {}) {
|
|
|
36531
37187
|
prompt: redactSessionMemoryCredentialText(createAnalyzerPrompt2(evidence)).value,
|
|
36532
37188
|
timeoutMs: input.timeoutMs ?? MODEL_TIMEOUT_MS3
|
|
36533
37189
|
});
|
|
36534
|
-
return parseSessionProposalAnalysis(JSON.parse(await
|
|
37190
|
+
return parseSessionProposalAnalysis(JSON.parse(await readFile36(outputPath, "utf8")));
|
|
36535
37191
|
} finally {
|
|
36536
37192
|
await rm14(workDir, { recursive: true, force: true });
|
|
36537
37193
|
}
|
|
@@ -36891,7 +37547,7 @@ async function releaseScanLock(lock) {
|
|
|
36891
37547
|
}
|
|
36892
37548
|
async function readScanLockOwner(path2) {
|
|
36893
37549
|
try {
|
|
36894
|
-
const value = JSON.parse(await
|
|
37550
|
+
const value = JSON.parse(await readFile36(path2, "utf8"));
|
|
36895
37551
|
return isRecord22(value) && typeof value.ownerId === "string" ? value.ownerId : null;
|
|
36896
37552
|
} catch {
|
|
36897
37553
|
return null;
|
|
@@ -36970,7 +37626,7 @@ function selectIndependentCandidateReceipts(candidates2) {
|
|
|
36970
37626
|
}
|
|
36971
37627
|
async function readReceipt(path2) {
|
|
36972
37628
|
try {
|
|
36973
|
-
const value = JSON.parse(await
|
|
37629
|
+
const value = JSON.parse(await readFile36(path2, "utf8"));
|
|
36974
37630
|
return isRecord22(value) && value.kind === "session-repo-proposal-receipt" ? value : null;
|
|
36975
37631
|
} catch {
|
|
36976
37632
|
return null;
|
|
@@ -36978,7 +37634,7 @@ async function readReceipt(path2) {
|
|
|
36978
37634
|
}
|
|
36979
37635
|
async function readDailyState(path2) {
|
|
36980
37636
|
try {
|
|
36981
|
-
const value = JSON.parse(await
|
|
37637
|
+
const value = JSON.parse(await readFile36(path2, "utf8"));
|
|
36982
37638
|
return isRecord22(value) && value.kind === "session-repo-proposal-daily-state" ? value : null;
|
|
36983
37639
|
} catch {
|
|
36984
37640
|
return null;
|
|
@@ -37428,14 +38084,14 @@ class EvolutionJobService {
|
|
|
37428
38084
|
});
|
|
37429
38085
|
await persist();
|
|
37430
38086
|
try {
|
|
37431
|
-
const retention2 = await
|
|
38087
|
+
const retention2 = await purgeExpiredSessionEvidence({
|
|
37432
38088
|
homeDir: this.#options.homeDir,
|
|
37433
38089
|
now: this.#now(),
|
|
37434
38090
|
limit: EVOLUTION_JOB_RETENTION_LIMIT
|
|
37435
38091
|
});
|
|
37436
38092
|
job = updateStage(job, "finalize", {
|
|
37437
38093
|
status: "completed",
|
|
37438
|
-
detail: `Evolution results are ready; ${retention2.purged} expired
|
|
38094
|
+
detail: `Evolution results are ready; ${retention2.purged} expired evidence segment(s) were purged.`,
|
|
37439
38095
|
counts: {
|
|
37440
38096
|
retentionScanned: retention2.scanned,
|
|
37441
38097
|
retentionDue: retention2.due,
|
|
@@ -37444,10 +38100,10 @@ class EvolutionJobService {
|
|
|
37444
38100
|
}
|
|
37445
38101
|
});
|
|
37446
38102
|
if (retention2.protected.length > 0) {
|
|
37447
|
-
job = addJobWarning(job, `${retention2.protected.length} overdue
|
|
38103
|
+
job = addJobWarning(job, `${retention2.protected.length} overdue evidence segment(s) remain protected by pending processing or review.`);
|
|
37448
38104
|
}
|
|
37449
38105
|
} catch (error) {
|
|
37450
|
-
job = addJobWarning(job, `
|
|
38106
|
+
job = addJobWarning(job, `Session Evidence retention failed: ${safeError3(error)}`);
|
|
37451
38107
|
job = updateStage(job, "finalize", {
|
|
37452
38108
|
status: "completed",
|
|
37453
38109
|
detail: "Evolution results are ready; retention maintenance reported a warning."
|
|
@@ -37649,7 +38305,7 @@ async function writeAtomicJson(path2, value) {
|
|
|
37649
38305
|
}
|
|
37650
38306
|
async function readJobFile(path2) {
|
|
37651
38307
|
try {
|
|
37652
|
-
const value = JSON.parse(await
|
|
38308
|
+
const value = JSON.parse(await readFile37(path2, "utf8"));
|
|
37653
38309
|
if (!isEvolutionJob(value))
|
|
37654
38310
|
return null;
|
|
37655
38311
|
return {
|
|
@@ -37708,7 +38364,7 @@ async function releaseEvolutionJobLock(lock) {
|
|
|
37708
38364
|
}
|
|
37709
38365
|
async function evolutionJobLockIsOwned(lock) {
|
|
37710
38366
|
try {
|
|
37711
|
-
const value = JSON.parse(await
|
|
38367
|
+
const value = JSON.parse(await readFile37(lock.path, "utf8"));
|
|
37712
38368
|
return value.jobId === lock.jobId;
|
|
37713
38369
|
} catch {
|
|
37714
38370
|
return false;
|
|
@@ -37752,7 +38408,7 @@ async function reconcileEvolutionJob(homeDir, job, now2) {
|
|
|
37752
38408
|
async function hasLiveEvolutionJobLock(homeDir, jobId) {
|
|
37753
38409
|
const path2 = join38(resolveEvoDevPaths(homeDir).stateDir, "evolution", ".job.lock");
|
|
37754
38410
|
try {
|
|
37755
|
-
const [value, info] = await Promise.all([
|
|
38411
|
+
const [value, info] = await Promise.all([readFile37(path2, "utf8"), stat26(path2)]);
|
|
37756
38412
|
const lock = JSON.parse(value);
|
|
37757
38413
|
return lock.jobId === jobId && Date.now() - info.mtimeMs <= EVOLUTION_JOB_LOCK_STALE_MS;
|
|
37758
38414
|
} catch {
|
|
@@ -37777,7 +38433,7 @@ import {
|
|
|
37777
38433
|
access as access2,
|
|
37778
38434
|
chmod as chmod3,
|
|
37779
38435
|
mkdir as mkdir27,
|
|
37780
|
-
readFile as
|
|
38436
|
+
readFile as readFile38,
|
|
37781
38437
|
realpath as realpath8,
|
|
37782
38438
|
rename as rename9,
|
|
37783
38439
|
rm as rm16,
|
|
@@ -37995,7 +38651,7 @@ function createStatus(state, path2, schedule = {
|
|
|
37995
38651
|
}
|
|
37996
38652
|
async function readInstalledSchedule(path2) {
|
|
37997
38653
|
try {
|
|
37998
|
-
const plist = await
|
|
38654
|
+
const plist = await readFile38(path2, "utf8");
|
|
37999
38655
|
const hour = /<key>Hour<\/key>\s*<integer>(\d{1,2})<\/integer>/u.exec(plist)?.[1];
|
|
38000
38656
|
const minute = /<key>Minute<\/key>\s*<integer>(\d{1,2})<\/integer>/u.exec(plist)?.[1];
|
|
38001
38657
|
if (plist.includes("<key>StartCalendarInterval</key>") && hour !== undefined) {
|
|
@@ -39342,7 +39998,7 @@ function safeError4(error) {
|
|
|
39342
39998
|
}
|
|
39343
39999
|
|
|
39344
40000
|
// packages/cli/src/ui/assets.ts
|
|
39345
|
-
import { readFile as
|
|
40001
|
+
import { readFile as readFile39 } from "node:fs/promises";
|
|
39346
40002
|
import { dirname as dirname32, join as join40 } from "node:path";
|
|
39347
40003
|
import { fileURLToPath as fileURLToPath4 } from "node:url";
|
|
39348
40004
|
var cachedAssets = null;
|
|
@@ -39375,15 +40031,15 @@ async function loadUiWebAssetsUncached() {
|
|
|
39375
40031
|
}
|
|
39376
40032
|
const distAssetsDir = join40(moduleDir, "ui");
|
|
39377
40033
|
return {
|
|
39378
|
-
script: await
|
|
39379
|
-
styles: await
|
|
40034
|
+
script: await readFile39(join40(distAssetsDir, "app.js"), "utf8"),
|
|
40035
|
+
styles: await readFile39(join40(distAssetsDir, "styles.css"), "utf8")
|
|
39380
40036
|
};
|
|
39381
40037
|
}
|
|
39382
40038
|
async function compileUiStyles(inputPath, options = {}) {
|
|
39383
40039
|
const [{ default: postcss }, { default: tailwindcss }, source] = await Promise.all([
|
|
39384
40040
|
import("postcss"),
|
|
39385
40041
|
import("@tailwindcss/postcss"),
|
|
39386
|
-
|
|
40042
|
+
readFile39(inputPath, "utf8")
|
|
39387
40043
|
]);
|
|
39388
40044
|
const result = await postcss([
|
|
39389
40045
|
tailwindcss({
|
|
@@ -39396,7 +40052,7 @@ async function compileUiStyles(inputPath, options = {}) {
|
|
|
39396
40052
|
}
|
|
39397
40053
|
async function fileExists2(path2) {
|
|
39398
40054
|
try {
|
|
39399
|
-
await
|
|
40055
|
+
await readFile39(path2, "utf8");
|
|
39400
40056
|
return true;
|
|
39401
40057
|
} catch {
|
|
39402
40058
|
return false;
|
|
@@ -39404,7 +40060,7 @@ async function fileExists2(path2) {
|
|
|
39404
40060
|
}
|
|
39405
40061
|
|
|
39406
40062
|
// packages/cli/src/ui/knowledge-detail.ts
|
|
39407
|
-
import { lstat as lstat5, readFile as
|
|
40063
|
+
import { lstat as lstat5, readFile as readFile40, realpath as realpath9 } from "node:fs/promises";
|
|
39408
40064
|
import { isAbsolute as isAbsolute14, relative as relative15 } from "node:path";
|
|
39409
40065
|
class UiKnowledgeDetailError extends Error {
|
|
39410
40066
|
code;
|
|
@@ -39429,7 +40085,7 @@ async function readUiKnowledgeConceptDetail(input) {
|
|
|
39429
40085
|
homeDir: input.homeDir,
|
|
39430
40086
|
path: concept.path
|
|
39431
40087
|
});
|
|
39432
|
-
const markdown = await
|
|
40088
|
+
const markdown = await readFile40(path2, "utf8");
|
|
39433
40089
|
if (detectSessionMemorySensitivity(markdown).classification === "credential") {
|
|
39434
40090
|
throw new UiKnowledgeDetailError("unsafe", "Knowledge detail is unavailable because the local file contains credential-like content.");
|
|
39435
40091
|
}
|
|
@@ -39774,7 +40430,7 @@ function isExecFileExitError(error) {
|
|
|
39774
40430
|
}
|
|
39775
40431
|
|
|
39776
40432
|
// packages/cli/src/ui/runtime.ts
|
|
39777
|
-
import { mkdir as mkdir28, readFile as
|
|
40433
|
+
import { mkdir as mkdir28, readFile as readFile41, rm as rm17, writeFile as writeFile28 } from "node:fs/promises";
|
|
39778
40434
|
import { join as join41 } from "node:path";
|
|
39779
40435
|
function resolveUiRuntimePaths(homeDir) {
|
|
39780
40436
|
const rootDir = join41(resolveEvoDevPaths(homeDir).stateDir, "ui");
|
|
@@ -39887,7 +40543,7 @@ function parseUiRuntimeToken(value) {
|
|
|
39887
40543
|
}
|
|
39888
40544
|
async function readOptionalFile(path2) {
|
|
39889
40545
|
try {
|
|
39890
|
-
return await
|
|
40546
|
+
return await readFile41(path2, "utf8");
|
|
39891
40547
|
} catch (error) {
|
|
39892
40548
|
if (isNodeError5(error) && error.code === "ENOENT")
|
|
39893
40549
|
return null;
|
|
@@ -39900,7 +40556,7 @@ function isNodeError5(error) {
|
|
|
39900
40556
|
|
|
39901
40557
|
// packages/cli/src/runtime-discovery.ts
|
|
39902
40558
|
import { spawn as spawn10 } from "node:child_process";
|
|
39903
|
-
import { readFile as
|
|
40559
|
+
import { readFile as readFile42, stat as stat28 } from "node:fs/promises";
|
|
39904
40560
|
import { join as join42 } from "node:path";
|
|
39905
40561
|
var MAX_CODEX_CONFIG_BYTES = 1024 * 1024;
|
|
39906
40562
|
var MAX_CODEX_CATALOG_BYTES = 8 * 1024 * 1024;
|
|
@@ -39973,7 +40629,7 @@ async function readAllowlistedCodexDefaults(codexHome) {
|
|
|
39973
40629
|
if (!metadata.isFile() || metadata.size > MAX_CODEX_CONFIG_BYTES) {
|
|
39974
40630
|
return { model: null, reasoningEffort: null };
|
|
39975
40631
|
}
|
|
39976
|
-
const config2 = await
|
|
40632
|
+
const config2 = await readFile42(path2, "utf8");
|
|
39977
40633
|
if (Buffer.byteLength(config2, "utf8") > MAX_CODEX_CONFIG_BYTES) {
|
|
39978
40634
|
return { model: null, reasoningEffort: null };
|
|
39979
40635
|
}
|
|
@@ -42279,15 +42935,15 @@ async function startUiServer(input) {
|
|
|
42279
42935
|
return;
|
|
42280
42936
|
}
|
|
42281
42937
|
const projectKey = evidenceMatch[1];
|
|
42282
|
-
const
|
|
42938
|
+
const sessionKey2 = evidenceMatch[2];
|
|
42283
42939
|
const segmentId = evidenceMatch[3];
|
|
42284
|
-
if (projectKey === undefined ||
|
|
42940
|
+
if (projectKey === undefined || sessionKey2 === undefined || segmentId === undefined) {
|
|
42285
42941
|
throw new UiRequestError(400, "session evidence identity is invalid");
|
|
42286
42942
|
}
|
|
42287
42943
|
const detail = await readUiSessionEvidenceDetail({
|
|
42288
42944
|
homeDir: input.homeDir,
|
|
42289
42945
|
projectKey,
|
|
42290
|
-
sessionKey,
|
|
42946
|
+
sessionKey: sessionKey2,
|
|
42291
42947
|
segmentId
|
|
42292
42948
|
});
|
|
42293
42949
|
writeJsonResponse(response, 200, { ok: true, data: detail });
|
|
@@ -43099,6 +43755,7 @@ function getHelpText() {
|
|
|
43099
43755
|
" evo review Start a local-only evolution review snapshot server",
|
|
43100
43756
|
" evo review decide Record a concrete repo-proposal decision with --yes",
|
|
43101
43757
|
" evo process --once Consume queued evolution triggers",
|
|
43758
|
+
" evo retention [--dry-run|--apply --yes] Preview or apply Session Evidence retention",
|
|
43102
43759
|
" evo import --source <source> --path <file> [--project <key>] (--dry-run [--json] | --apply <preview-id> --yes) Preview or apply bounded historical evidence",
|
|
43103
43760
|
" schedule status Show OS schedule, automation settings, and pending work",
|
|
43104
43761
|
" schedule install|uninstall Manage the daily macOS user evolution LaunchAgent",
|
|
@@ -43110,7 +43767,7 @@ function getHelpText() {
|
|
|
43110
43767
|
" config set evolution.automation.recommendations true|false Toggle scheduled model-backed recommendations",
|
|
43111
43768
|
" config set evolution.schedule.dailyTime HH:MM Set the system-local daily trigger time",
|
|
43112
43769
|
" config set memory.reviewKnowledgeUpdates true|false Review runtime-affecting knowledge updates",
|
|
43113
|
-
" config set memory.runtimeInjection true|false Toggle
|
|
43770
|
+
" config set memory.runtimeInjection true|false Toggle support-backed knowledge runtime delivery",
|
|
43114
43771
|
" config set memory.staleReview true|false Toggle review-due reminders",
|
|
43115
43772
|
" config set teamRuntime.defaultRuntime codex|claude Set the fallback for future team roles",
|
|
43116
43773
|
" config set teamRuntime.defaultModel <model>|default Set the future team model fallback",
|
|
@@ -43543,20 +44200,20 @@ async function runPostCommandMaintenanceSafely(argv, options, debug) {
|
|
|
43543
44200
|
await appendDebugLogSafely(options, debug, {
|
|
43544
44201
|
level: "info",
|
|
43545
44202
|
category: "maintenance.retention",
|
|
43546
|
-
message: "
|
|
44203
|
+
message: "session evidence retention maintenance completed",
|
|
43547
44204
|
argv,
|
|
43548
44205
|
data: {
|
|
43549
|
-
scanned: result.
|
|
43550
|
-
due: result.
|
|
43551
|
-
purged: result.
|
|
43552
|
-
protected: result.
|
|
44206
|
+
scanned: result.sessionEvidence.scanned,
|
|
44207
|
+
due: result.sessionEvidence.due,
|
|
44208
|
+
purged: result.sessionEvidence.purged,
|
|
44209
|
+
protected: result.sessionEvidence.protected.length
|
|
43553
44210
|
}
|
|
43554
44211
|
});
|
|
43555
44212
|
} catch (error) {
|
|
43556
44213
|
await appendDebugLogSafely(options, debug, {
|
|
43557
44214
|
level: "warn",
|
|
43558
44215
|
category: "maintenance.retention",
|
|
43559
|
-
message: "
|
|
44216
|
+
message: "session evidence retention maintenance failed",
|
|
43560
44217
|
argv,
|
|
43561
44218
|
data: {
|
|
43562
44219
|
error: formatCommandError(error)
|