@evo-dev/core 0.0.1-alpha.19 → 0.0.1-alpha.20

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