@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/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 readFile11 } from "node:fs/promises";
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 readFile10, readdir as readdir8, rename as rename3, rm as rm4, stat as stat5, writeFile as writeFile7 } from "node:fs/promises";
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 readFile8 } from "node:fs/promises";
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
- assertHistoricalRetentionState(input.segment);
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 assertHistoricalRetentionState(segment) {
3007
- if (segment.origin?.kind !== "historical-import" || segment.retention === undefined) {
3008
- throw new Error("Atomic Session Evidence rewrite is limited to historical retention.");
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 historical Session Evidence raw state is invalid.");
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 historical Session Evidence raw state is invalid.");
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 = 20;
3235
- async function inspectHistoricalSessionEvidenceRetention(input) {
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 historical = (await listSessionEvidenceSegments({
3278
+ const segments = (await listSessionEvidenceSegments({
3238
3279
  homeDir: input.homeDir,
3239
3280
  projectKey: input.projectKey
3240
- })).filter((segment) => segment.origin?.kind === "historical-import" && segment.retention !== undefined);
3241
- const result = {
3242
- available: 0,
3243
- purged: 0,
3244
- overdue: 0,
3245
- purgeEligible: 0,
3246
- protected: 0,
3247
- protectionReasons: {}
3248
- };
3249
- const reviewSnapshots = new Map;
3250
- for (const segment of historical) {
3251
- if (segment.retention?.rawState === "purged") {
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.protected += 1;
3259
- result.protectionReasons["retention-invalid"] = (result.protectionReasons["retention-invalid"] ?? 0) + 1;
3313
+ addProtection(result, "retention-invalid");
3260
3314
  continue;
3261
3315
  }
3262
- if (segment.retention === undefined || Date.parse(segment.retention.expiresAt) > Date.parse(now)) {
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
- reviewSnapshots
3322
+ context
3270
3323
  });
3271
- if (protection === null) {
3324
+ if (protection === null)
3272
3325
  result.purgeEligible += 1;
3273
- continue;
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 purgeExpiredHistoricalSessionEvidence(input) {
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 historical = (await listSessionEvidenceSegments({
3337
+ const segments = (await listSessionEvidenceSegments({
3284
3338
  homeDir: input.homeDir,
3285
3339
  projectKey: input.projectKey
3286
- })).filter((segment) => segment.origin?.kind === "historical-import" && segment.retention !== undefined).sort((left, right) => (left.retention?.expiresAt ?? "").localeCompare(right.retention?.expiresAt ?? "") || left.id.localeCompare(right.id));
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: 0,
3345
+ alreadyPurged: segments.filter((segment) => segment.retention?.rawState === "purged").length,
3292
3346
  protected: []
3293
3347
  };
3294
- const reviewSnapshots = new Map;
3295
- result.alreadyPurged = historical.filter((segment) => segment.retention?.rawState === "purged").length;
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
- reviewSnapshots
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
- const currentTrigger = await findSegmentTrigger(input.homeDir, current);
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 freshSnapshot = await readEvolutionReviewSnapshot({
3394
+ const freshProtection = await findRetentionProtection({
3343
3395
  homeDir: input.homeDir,
3344
- projectKey: current.projectKey
3396
+ segment: current,
3397
+ context: createRetentionContext()
3345
3398
  });
3346
- if (hasPendingDerivedReview(freshSnapshot, current)) {
3399
+ if (freshProtection !== null) {
3347
3400
  result.protected.push({
3348
- projectKey: segment.projectKey,
3349
- segmentId: segment.id,
3350
- reason: "derived-review-pending"
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: createPurgedHistoricalSegment(current, now)
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(`Historical Session Evidence retention limit must be between 1 and ${MAX_RETENTION_LIMIT}.`);
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
- const matches = (await listSegmentEvolutionTriggers({
3394
- homeDir,
3395
- projectKey: segment.projectKey
3396
- })).filter((trigger) => trigger.segmentId === segment.id && trigger.sessionKey === segment.sessionKey);
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 createPurgedHistoricalSegment(segment, now) {
3564
+ function createPurgedSegment(segment, now) {
3416
3565
  const retention = segment.retention;
3417
3566
  if (retention === undefined || retention.rawState !== "available") {
3418
- throw new Error("Historical Session Evidence raw body is not available for purge.");
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 readFile5, readdir as readdir5, realpath, stat as stat2 } from "node:fs/promises";
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 sessionKey = sanitizePersistentIdentifier(input.sessionKey, "session");
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 readFile5(path, "utf8")));
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 readFile5(path, "utf8")));
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 readFile5(path, "utf8")));
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 readFile5(path, "utf8")));
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 readFile5(join8(root, sessionDir, "binding.json"), "utf8"));
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: sha256Hex(truncated.content)
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 sessionKey = sanitizeStorageId(resolveTraceSessionKey(input.rawPayload), "session");
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, sessionKey, paths.eventsPath, now);
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
- const trigger = await enqueueSegmentEvolutionTrigger({
4900
- homeDir: input.homeDir,
4901
- projectKey: segment.projectKey,
4902
- sessionKey: segment.sessionKey,
4903
- runId: segment.runId,
4904
- roleId: segment.roleId,
4905
- segmentId: segment.id,
4906
- segmentPath,
4907
- strength: segment.strength,
4908
- reason: segment.reason,
4909
- summary: segment.normalized.summary,
4910
- now
4911
- });
4912
- queuedSegmentTriggerId = trigger.id;
4913
- cursor.lastCapturedLine = appended.lineNumber;
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 readFile7, writeFile as writeFile5 } from "node:fs/promises";
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 readFile6, readdir as readdir6, stat as stat3, writeFile as writeFile4 } from "node:fs/promises";
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 readFile6(paths2.runPath(runId), "utf8")));
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 readFile6(paths2.latestRunPath, "utf8"));
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 readFile6(paths2.legacyLatestRunPath, "utf8"));
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 readFile6(paths2.agentPath(runId, roleId), "utf8")));
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) => readFile6(join10(paths2.agentsDir(runId), entry), "utf8").then((raw) => parseTeamAgentRecord(JSON.parse(raw)))));
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 readFile6(paths2.messageListPath(runId), "utf8")));
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 readFile6(repoTeamPath, "utf8"))
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 readFile6(globalTeamPath, "utf8"))
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 readFile6(asset.sourcePath, "utf8");
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 sessionKey = `team-${input.runId}-${input.role.roleId}`;
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 readFile6(settingsPath, "utf8")));
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 readFile6(path, "utf8");
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 readFile6(reference.sourcePath, "utf8");
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 readFile6(path, "utf8");
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 readFile6(path, "utf8");
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 readFile6(path, "utf8");
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
- const completed = appendRuntimeDiagnostics(appendRuntimeDiagnostics(appendRuntimeDiagnostics(appendRuntimeDiagnostics(appendRuntimeDiagnostics(appendRuntimeDiagnostics(appendRuntimeDiagnostics(result, diagnostics), projectDiagnostics), sessionMemoryDiagnostics), evolutionDiagnostics), messageDiagnostics), scopedContextDiagnostics), teamStateDiagnostics);
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 || team.roleId !== "main")
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 sessionKey = resolveHookSessionKey(input.rawPayload);
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 sessionKey = resolveHookSessionKey(input.rawPayload);
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 sessionKey = resolveHookSessionKey(payload);
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 readFile7(paths2.bindingPath, "utf8"));
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 readFile8(paths2.settingsPath, "utf8"))).memory;
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 readFile9,
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 readFile9(join12(paths2.candidatesDir, name), "utf8"));
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 readFile9(path, "utf8")));
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 readFile9(path, "utf8")));
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 readFile9(path, "utf8"));
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 readFile9(path, "utf8"));
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/freshness.ts
9305
- function deriveOkfKnowledgeFreshness(input) {
9686
+ // packages/core/src/evolution/knowledge/support.ts
9687
+ function resolveOkfKnowledgeRuntimeEligibility(input) {
9306
9688
  const now = normalizeNow(input.now);
9307
- const concept = input.concept;
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 status or staleAfter excludes this concept from new runtime context."
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 (concept.verificationSnapshot === null) {
9696
+ if (input.reviewState !== "accepted" && input.reviewState !== "auto-accepted") {
9315
9697
  return {
9698
+ delivery: "blocked",
9316
9699
  freshness: "unknown",
9317
- reason: "No verifiable knowledge snapshot is stored for this legacy concept."
9700
+ reason: "Knowledge has not passed an active-compatible review state."
9318
9701
  };
9319
9702
  }
9320
- if (input.repositoryFingerprintChanged === true || isDue(concept.lifecycle.reviewAfter, now)) {
9703
+ if (input.sourceStatus === "changed") {
9321
9704
  return {
9705
+ delivery: "blocked",
9322
9706
  freshness: "review-due",
9323
- reason: input.repositoryFingerprintChanged === true ? "A checked repository path changed after the last verification." : `Review was due at ${concept.lifecycle.reviewAfter}.`
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: `Verified at ${concept.verificationSnapshot.verifiedAt}.`
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
- const hasVerification = input.candidate.bodySections.verification.length > 0 || input.candidate.verificationNotApplicableReason !== undefined;
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 readFile10(targetPath, "utf8"));
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 readFile10(path, "utf8"));
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 readFile10(file.path, "utf8");
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 readFile10(file.path, "utf8");
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: "verified",
11213
- runtimeExcerpt: sanitizeOkfText(evosCase.expectedFutureBehavior || evosCase.result.summary || evosCase.title).slice(0, 1200),
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(input);
11227
- if (result.items.length === 0)
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 = result.items.map((item) => ({
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
- queryText: queryText ?? null,
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
- ...queryText === undefined ? {} : { queryText },
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 readFile10(file.path, "utf8");
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 readFile10(file.path, "utf8");
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 readFile10(input.overlayPath, "utf8");
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 readFile10(filePath, "utf8"));
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 readFile10(logPath, "utf8") : `# Directory Update Log
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 readFile10(file.path, "utf8");
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 readFile10(file.path, "utf8");
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 readFile10(file.path, "utf8");
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 readFile11(path, "utf8")));
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 readFile12, readdir as readdir9, stat as stat6 } from "node:fs/promises";
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 readFile12(manifestPath, "utf8");
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 readFile13, readdir as readdir10, stat as stat7, writeFile as writeFile8 } from "node:fs/promises";
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 sessionKey = sanitizePersistentIdentifier2(input.sessionKey, "session");
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}-${sessionKey}`,
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 readFile13(path, "utf8"))));
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 readFile13(path, "utf8"))));
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 readFile14, writeFile as writeFile9 } from "node:fs/promises";
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 readFile14(filePath, "utf8");
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 readFile14(filePath, "utf8");
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 readFile14(filePath, "utf8");
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 readFile18, readdir as readdir13, rm as rm6, stat as stat10, writeFile as writeFile11 } from "node:fs/promises";
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 readFile15, readdir as readdir11 } from "node:fs/promises";
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 readFile15(input.path, "utf8");
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 readFile15(input.path, "utf8");
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 readFile16, rm as rm5, stat as stat8 } from "node:fs/promises";
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 readFile16(lock.path, "utf8"));
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 readFile17, readdir as readdir12, stat as stat9, writeFile as writeFile10 } from "node:fs/promises";
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 readFile17(path, "utf8")).split(`
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 readFile18(paths2.lockPath, "utf8"));
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 readFile18(paths2.tokenPath, "utf8")).trim();
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 readFile18(path, "utf8")).split(`
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 readFile18(path, "utf8"));
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 readFile19, rename as rename4, rm as rm7 } from "node:fs/promises";
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 readFile19(path, "utf8");
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 readFile19(lock.ownerPath, "utf8"));
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 readFile19(path, "utf8"));
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 readFile19(path, "utf8");
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 readFile19(path, "utf8");
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 = addDays(createdAt, input.plan.policy.retentionDays);
17944
- const sessionKey = `historical-${input.plan.preview.sourceKey}`;
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 addDays(value, days) {
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 sessionKey = `historical-${input.plan.preview.sourceKey}`;
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, sessionKey, segment.id]));
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 sessionKey = `historical-${manifest.sourceKey}`;
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 === sessionKey && progress.expectedSegmentIds.includes(trigger.segmentId)).map((trigger) => trigger.id));
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 readFile20, stat as stat11, writeFile as writeFile12 } from "node:fs/promises";
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 readFile20(path, "utf8");
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 readFile21, readdir as readdir14, stat as stat12 } from "node:fs/promises";
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 readFile21(manifestPath, "utf8")));
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 readFile22, writeFile as writeFile13 } from "node:fs/promises";
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 readFile23, readdir as readdir15 } from "node:fs/promises";
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 readFile23(manifestPath, "utf8"))));
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 readFile26, readdir as readdir18, stat as stat16, writeFile as writeFile18 } from "node:fs/promises";
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 readFile24, readdir as readdir16, stat as stat14, writeFile as writeFile16 } from "node:fs/promises";
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 readFile24(path2, "utf8");
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 readFile25, readdir as readdir17, rm as rm9, stat as stat15, writeFile as writeFile17 } from "node:fs/promises";
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 readFile25(sourcePackagePath, "utf8"));
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 readFile26(path2, "utf8");
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 readFile26(path2, "utf8");
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 readFile27, stat as stat17, writeFile as writeFile19 } from "node:fs/promises";
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 readFile27(path2, "utf8");
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.19";
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 inspectHistoricalSessionEvidenceRetention({ homeDir });
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: "historical evidence retention",
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: "historical evidence retention",
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 ? "Run a normal write or scheduled evolution command to perform bounded local retention maintenance." : "Finish or explicitly reject/defer pending historical evidence processing and review."
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: "historical evidence retention",
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 readFile29, realpath as realpath4, rm as rm10, stat as stat21, writeFile as writeFile20 } from "node:fs/promises";
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 readFile28, realpath as realpath3, stat as stat20 } from "node:fs/promises";
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 readFile28(path2, "utf8")), truncated: false };
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 readFile29(outputPath, "utf8")));
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 requiresHumanReview = candidate.operation === "revoke" || candidate.requiresHumanReview || candidate.basis === "inferred" || /security|privacy|release|architecture|cross[-_ ]?repo/i.test(`${candidate.kind} ${candidate.title} ${candidate.claim}`);
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 readFile30, stat as stat22 } from "node:fs/promises";
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 readFile30(flags.from, "utf8"));
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 sessionKey = input.payload === null ? "session-local" : resolveTraceSessionKey(input.payload);
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 readFile31, writeFile as writeFile21 } from "node:fs/promises";
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 readFile31(path2, "utf8")));
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
- historicalEvidence: await purgeExpiredHistoricalSessionEvidence({
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 readFile32, rename as rename6, rm as rm11, writeFile as writeFile22 } from "node:fs/promises";
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 readFile32(resolveDailyScheduleStatePath(homeDir), "utf8"));
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 readFile36, readdir as readdir21, rename as rename8, rm as rm15, stat as stat26, utimes, writeFile as writeFile26 } from "node:fs/promises";
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 readFile33, realpath as realpath5, rm as rm12, stat as stat23, writeFile as writeFile23 } from "node:fs/promises";
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 readFile33(outputPath, "utf8"))),
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 readFile35,
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 readFile34, readdir as readdir19, realpath as realpath6, rename as rename7, rm as rm13, stat as stat24, writeFile as writeFile24 } from "node:fs/promises";
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 readFile34(join36(dir, file), "utf8"))));
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 readFile34(path2, "utf8"));
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 readFile35(outputPath, "utf8")));
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 readFile35(path2, "utf8"));
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 readFile35(path2, "utf8"));
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 readFile35(path2, "utf8"));
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 purgeExpiredHistoricalSessionEvidence({
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 historical evidence segment(s) were purged.`,
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 historical evidence segment(s) remain protected by pending processing or review.`);
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, `Historical evidence retention failed: ${safeError3(error)}`);
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 readFile36(path2, "utf8"));
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 readFile36(lock.path, "utf8"));
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([readFile36(path2, "utf8"), stat26(path2)]);
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 readFile37,
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 readFile37(path2, "utf8");
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 readFile38 } from "node:fs/promises";
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 readFile38(join40(distAssetsDir, "app.js"), "utf8"),
39379
- styles: await readFile38(join40(distAssetsDir, "styles.css"), "utf8")
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
- readFile38(inputPath, "utf8")
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 readFile38(path2, "utf8");
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 readFile39, realpath as realpath9 } from "node:fs/promises";
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 readFile39(path2, "utf8");
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 readFile40, rm as rm17, writeFile as writeFile28 } from "node:fs/promises";
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 readFile40(path2, "utf8");
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 readFile41, stat as stat28 } from "node:fs/promises";
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 readFile41(path2, "utf8");
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 sessionKey = evidenceMatch[2];
42938
+ const sessionKey2 = evidenceMatch[2];
42283
42939
  const segmentId = evidenceMatch[3];
42284
- if (projectKey === undefined || sessionKey === undefined || segmentId === 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 verified knowledge runtime delivery",
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: "historical evidence retention maintenance completed",
44203
+ message: "session evidence retention maintenance completed",
43547
44204
  argv,
43548
44205
  data: {
43549
- scanned: result.historicalEvidence.scanned,
43550
- due: result.historicalEvidence.due,
43551
- purged: result.historicalEvidence.purged,
43552
- protected: result.historicalEvidence.protected.length
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: "historical evidence retention maintenance failed",
44216
+ message: "session evidence retention maintenance failed",
43560
44217
  argv,
43561
44218
  data: {
43562
44219
  error: formatCommandError(error)