@tangle-network/agent-knowledge 3.1.0 → 3.2.1

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
@@ -55,7 +55,7 @@ import {
55
55
  writeJsonDurableWithinRoot,
56
56
  writeKnowledgeIndex,
57
57
  writeSourceRegistry
58
- } from "./chunk-2BJ5JR3L.js";
58
+ } from "./chunk-EUAXSBMH.js";
59
59
  import {
60
60
  AgentMemoryHitSchema,
61
61
  AgentMemoryKindSchema,
@@ -653,7 +653,7 @@ function createAdaptiveResearchDriver(options = {}) {
653
653
 
654
654
  // src/agent-candidate.ts
655
655
  import {
656
- sha256DigestSchema
656
+ sha256DigestSchema as sha256DigestSchema2
657
657
  } from "@tangle-network/agent-interface";
658
658
 
659
659
  // src/kb-improvement.ts
@@ -666,6 +666,13 @@ import {
666
666
  contentHash,
667
667
  validateRunRecord
668
668
  } from "@tangle-network/agent-eval";
669
+ import {
670
+ agentImprovementActivationResultSchema,
671
+ agentImprovementActivationSchema,
672
+ canonicalCandidateDigest,
673
+ omitTopLevelDigest,
674
+ sha256DigestSchema
675
+ } from "@tangle-network/agent-interface";
669
676
  import { z } from "zod";
670
677
 
671
678
  // src/claim-grounding.ts
@@ -2022,6 +2029,20 @@ function assertNotAborted(signal) {
2022
2029
  var digestSchema = z.string().regex(/^[a-f0-9]{64}$/);
2023
2030
  var runIdSchema = z.string().min(1).max(2048);
2024
2031
  var safePathSegmentSchema = z.string().min(1).max(128).regex(/^[A-Za-z0-9][A-Za-z0-9._-]*$/);
2032
+ var knowledgeImprovementMutationReceiptSchema = z.object({
2033
+ target: z.enum(["candidate", "baseline"]),
2034
+ beforeHash: digestSchema,
2035
+ afterHash: digestSchema,
2036
+ changed: z.boolean(),
2037
+ transactionId: z.string().uuid().nullable(),
2038
+ recovered: z.boolean()
2039
+ }).strict();
2040
+ var knowledgeImprovementActivationRecordSchema = z.object({
2041
+ kind: z.literal("knowledge-improvement-activation-result"),
2042
+ candidateId: safePathSegmentSchema,
2043
+ mutation: knowledgeImprovementMutationReceiptSchema,
2044
+ result: agentImprovementActivationResultSchema
2045
+ }).strict();
2025
2046
  var improvementStatusSchema = z.enum([
2026
2047
  "running",
2027
2048
  "candidate-ready",
@@ -2268,6 +2289,23 @@ async function loadKnowledgeImprovementState(root, runId) {
2268
2289
  throw error;
2269
2290
  }
2270
2291
  }
2292
+ async function loadKnowledgeImprovementActivationResult(options) {
2293
+ assertExactCandidatePlatform();
2294
+ const candidate = Object.freeze(KnowledgeImprovementCandidateRefSchema.parse(options.candidate));
2295
+ const activation = verifyCanonicalKnowledgeActivation(options.activation);
2296
+ const target = targetForKnowledgeActivation(activation);
2297
+ assertKnowledgeActivationAuthority(activation, candidate, target, options.identity);
2298
+ return withKnowledgeImprovementRun(options.root, candidate.runId, false, async (runDir) => {
2299
+ const record = await loadKnowledgeActivationRecord(
2300
+ runDir,
2301
+ candidate,
2302
+ activation,
2303
+ target,
2304
+ options.identity
2305
+ );
2306
+ return record?.result ?? null;
2307
+ });
2308
+ }
2271
2309
  async function loadKnowledgeImprovementStateFromRun(root, runId, runDir) {
2272
2310
  const stateFile = await readRegularFileWithinRoot(runDir, "state.json");
2273
2311
  const raw = JSON.parse(stateFile.bytes.toString("utf8"));
@@ -2287,9 +2325,46 @@ function knowledgeImprovementCandidateRef(result) {
2287
2325
  if (!result.candidate) throw new Error("knowledge improvement result has no candidate");
2288
2326
  return candidateRefFor(result.runId, result.state, result.candidate);
2289
2327
  }
2328
+ async function withKnowledgeImprovementComparison(options, use) {
2329
+ assertExactCandidatePlatform();
2330
+ const reference = Object.freeze(KnowledgeImprovementCandidateRefSchema.parse(options.candidate));
2331
+ return withKnowledgeImprovementRun(options.root, reference.runId, false, async (runDir) => {
2332
+ const state = await loadKnowledgeImprovementStateFromRun(options.root, reference.runId, runDir);
2333
+ return withMeasuredCandidateSnapshot(
2334
+ options.root,
2335
+ runDir,
2336
+ state,
2337
+ reference,
2338
+ (resolved) => withBaselineSnapshot(
2339
+ runDir,
2340
+ reference.baseHash,
2341
+ (baselineRoot) => withIsolatedKnowledgeCopy(
2342
+ baselineRoot,
2343
+ reference.baseHash,
2344
+ "baseline",
2345
+ (baseline) => withIsolatedKnowledgeCopy(
2346
+ resolved.root,
2347
+ reference.candidateHash,
2348
+ "candidate",
2349
+ (candidate) => use(
2350
+ Object.freeze({
2351
+ reference,
2352
+ evaluation: immutableJsonValue(structuredClone(resolved.evidence.evaluation)),
2353
+ baseline: Object.freeze({ root: baseline, hash: reference.baseHash }),
2354
+ candidate: Object.freeze({ root: candidate, hash: reference.candidateHash })
2355
+ })
2356
+ )
2357
+ )
2358
+ )
2359
+ )
2360
+ );
2361
+ });
2362
+ }
2290
2363
  async function withKnowledgeImprovementCandidate(options, use) {
2291
2364
  assertExactCandidatePlatform();
2292
- const candidateRef = KnowledgeImprovementCandidateRefSchema.parse(options.candidate);
2365
+ const candidateRef = Object.freeze(
2366
+ KnowledgeImprovementCandidateRefSchema.parse(options.candidate)
2367
+ );
2293
2368
  return withKnowledgeImprovementRun(options.root, candidateRef.runId, false, async (runDir) => {
2294
2369
  const state = await loadKnowledgeImprovementStateFromRun(
2295
2370
  options.root,
@@ -2301,14 +2376,17 @@ async function withKnowledgeImprovementCandidate(options, use) {
2301
2376
  runDir,
2302
2377
  state,
2303
2378
  candidateRef,
2304
- (resolved) => withIsolatedCandidateCopy(
2379
+ (resolved) => withIsolatedKnowledgeCopy(
2305
2380
  resolved.root,
2306
2381
  candidateRef.candidateHash,
2307
- (root) => use({
2308
- root,
2309
- candidate: candidateRef,
2310
- evaluation: resolved.evidence.evaluation
2311
- })
2382
+ "candidate",
2383
+ (root) => use(
2384
+ Object.freeze({
2385
+ root,
2386
+ candidate: candidateRef,
2387
+ evaluation: immutableJsonValue(structuredClone(resolved.evidence.evaluation))
2388
+ })
2389
+ )
2312
2390
  )
2313
2391
  );
2314
2392
  });
@@ -2325,6 +2403,7 @@ async function transitionKnowledgeCandidate(options, target) {
2325
2403
  KnowledgeImprovementCandidateRefSchema.parse(options.candidate)
2326
2404
  );
2327
2405
  const now = options.now ?? (() => /* @__PURE__ */ new Date());
2406
+ const activation = options.activation ? resolveKnowledgeActivationPersistence(options.activation, candidateRef, target) : void 0;
2328
2407
  return withKnowledgeImprovementRun(options.root, candidateRef.runId, false, async (runDir) => {
2329
2408
  const lease = await acquireRunLease(runDir, {
2330
2409
  ownerId: options.ownerId ?? `pid-${process.pid}`,
@@ -2346,7 +2425,8 @@ async function transitionKnowledgeCandidate(options, target) {
2346
2425
  leaseTtlMs: options.leaseTtlMs ?? DEFAULT_LEASE_TTL_MS,
2347
2426
  assertRunOwned: lease.assertOwned,
2348
2427
  now,
2349
- onState: options.onState
2428
+ onState: options.onState,
2429
+ activation
2350
2430
  },
2351
2431
  target
2352
2432
  );
@@ -2405,33 +2485,32 @@ async function improveKnowledgeBaseInRun(options, runId, runDir, now) {
2405
2485
  if (state.status === "promoted" && !promotedCandidate) {
2406
2486
  throw new Error("promoted knowledge state has no promoted candidate");
2407
2487
  }
2408
- const resumablePromotion = state.candidates.find(
2409
- (candidate2) => (candidate2.status === "candidate-ready" || candidate2.status === "promoted") && candidate2.evidenceHash !== void 0 && candidate2.promotionPlanHash !== void 0
2410
- );
2411
- const resumableCandidateRef = resumablePromotion ? candidateRefFor(runId, state, resumablePromotion) : void 0;
2412
- await withKnowledgeMutation(options.root, () => void 0, {
2413
- resumeTransaction: resumableCandidateRef ? {
2414
- purpose: knowledgeCandidateTransitionPurpose(resumableCandidateRef, "candidate"),
2415
- validate: (transaction) => assertCandidateTransitionTransaction(transaction, resumableCandidateRef, "candidate")
2416
- } : void 0
2417
- });
2418
- await ensureBaselineSnapshot(runDir, options.root, state.baseHash);
2419
2488
  if (state.status === "promoted") {
2420
2489
  const promoted = promotedCandidate;
2421
- return await applyKnowledgeCandidateTarget(
2422
- {
2423
- root: options.root,
2490
+ const promotedState = state;
2491
+ return withKnowledgeMutation(options.root, async () => {
2492
+ const currentHash = await hashKnowledgeBase(options.root);
2493
+ if (currentHash !== promoted.candidateHash) {
2494
+ throw new Error(
2495
+ `promoted knowledge base changed: expected ${promoted.candidateHash}, got ${currentHash}`
2496
+ );
2497
+ }
2498
+ const evidence2 = await assertCandidateEvidence(
2424
2499
  runDir,
2425
- state,
2426
- candidateRef: candidateRefFor(runId, state, promoted),
2427
- leaseTtlMs: options.leaseTtlMs ?? DEFAULT_LEASE_TTL_MS,
2428
- assertRunOwned: lease.assertOwned,
2429
- now,
2430
- onState: options.onState
2431
- },
2432
- "candidate"
2433
- );
2500
+ candidateRefFor(runId, promotedState, promoted)
2501
+ );
2502
+ return {
2503
+ runId,
2504
+ state: promotedState,
2505
+ candidate: promoted,
2506
+ evaluation: evidence2.evaluation,
2507
+ promoted: true,
2508
+ blocked: false
2509
+ };
2510
+ });
2434
2511
  }
2512
+ await withKnowledgeMutation(options.root, () => void 0);
2513
+ await ensureBaselineSnapshot(runDir, options.root, state.baseHash);
2435
2514
  if (state.status === "blocked") {
2436
2515
  return { runId, state, promoted: false, blocked: true };
2437
2516
  }
@@ -2532,19 +2611,85 @@ async function applyKnowledgeCandidateTarget(input, target) {
2532
2611
  const { candidateRef, runDir, state } = input;
2533
2612
  assertStateIdentity(input.root, candidateRef, state);
2534
2613
  const candidate = state.candidates.find((entry) => entry.candidateId === candidateRef.candidateId);
2535
- if (!candidate || canonicalJson(candidateRefFor(candidateRef.runId, state, candidate)) !== canonicalJson(candidateRef)) {
2614
+ if (!candidate || canonicalJson(candidateIdentityFor(candidateRef.runId, state, candidate)) !== canonicalJson(candidateRef)) {
2536
2615
  throw new Error("knowledge candidate approval does not match the measured candidate");
2537
2616
  }
2538
2617
  const action = target === "candidate" ? "promotion" : "restore";
2539
2618
  const desiredHash = target === "candidate" ? candidateRef.candidateHash : candidateRef.baseHash;
2540
2619
  const purpose = knowledgeCandidateTransitionPurpose(candidateRef, target);
2620
+ const recoveryOwner = input.activation ? `knowledge-improvement-activation:${input.activation.activation.digest}` : "knowledge-improvement-candidate-transition";
2541
2621
  return withKnowledgeMutation(
2542
2622
  input.root,
2543
2623
  async (mutationLock) => {
2544
- input.assertRunOwned();
2624
+ const assertOwned = () => {
2625
+ mutationLock.assertOwned();
2626
+ input.assertRunOwned();
2627
+ };
2628
+ assertOwned();
2545
2629
  const transactionRoot = mutationLock.transactionRoot;
2546
- let pending = null;
2630
+ const recovery = mutationLock.recovery?.purpose === purpose ? mutationLock.recovery : void 0;
2631
+ const existingActivation = input.activation ? await loadKnowledgeActivationRecord(
2632
+ runDir,
2633
+ candidateRef,
2634
+ input.activation.activation,
2635
+ target,
2636
+ input.activation.identity
2637
+ ) : null;
2638
+ if (existingActivation) {
2639
+ if (recovery?.direction === "rollback") {
2640
+ throw new Error("stored knowledge activation result conflicts with a pending rollback");
2641
+ }
2642
+ if (recovery) {
2643
+ const recoveredHash = await hashKnowledgeBase(input.root);
2644
+ if (recoveredHash !== existingActivation.mutation.afterHash) {
2645
+ throw new Error("stored knowledge activation result does not match the recovered files");
2646
+ }
2647
+ await finishKnowledgeFileTransaction({
2648
+ root: input.root,
2649
+ transactionRoot,
2650
+ transaction: recovery.transaction,
2651
+ assertOwned
2652
+ });
2653
+ }
2654
+ if (existingActivation.result.outcome.status === "conflict") {
2655
+ const reason = candidateTransitionConflictReason(
2656
+ target,
2657
+ candidateRef,
2658
+ existingActivation.mutation.afterHash
2659
+ );
2660
+ const blocked = await blockCandidateTransition(
2661
+ input,
2662
+ candidate,
2663
+ target,
2664
+ reason,
2665
+ existingActivation.mutation.afterHash
2666
+ );
2667
+ return { ...blocked, activationResult: existingActivation.result };
2668
+ }
2669
+ return candidateTransitionResult(
2670
+ input,
2671
+ candidate,
2672
+ target === "candidate" && state.status === "promoted",
2673
+ false,
2674
+ existingActivation.mutation,
2675
+ existingActivation.result
2676
+ );
2677
+ }
2678
+ if (recovery?.direction === "rollback") {
2679
+ await finishKnowledgeFileTransaction({
2680
+ root: input.root,
2681
+ transactionRoot,
2682
+ transaction: recovery.transaction,
2683
+ assertOwned
2684
+ });
2685
+ }
2686
+ const recovered = recovery?.direction === "apply" ? recovery : void 0;
2687
+ let pending = input.activation && recovered ? recovered.transaction : null;
2547
2688
  const currentHash = await hashKnowledgeBase(input.root);
2689
+ let transactionId = recovered?.transactionId ?? null;
2690
+ if (recovered && currentHash !== desiredHash) {
2691
+ throw new Error(`recovered knowledge ${action} does not match the approved target`);
2692
+ }
2548
2693
  if (state.status === "promoted" && state.promotedCandidateId !== candidate.candidateId) {
2549
2694
  throw new Error(
2550
2695
  `knowledge run already promoted '${state.promotedCandidateId ?? "unknown"}'`
@@ -2562,9 +2707,31 @@ async function applyKnowledgeCandidateTarget(input, target) {
2562
2707
  }
2563
2708
  if (currentHash !== state.baseHash && currentHash !== candidateRef.candidateHash) {
2564
2709
  const reason = target === "candidate" ? `base changed before promotion: expected ${state.baseHash}, got ${currentHash}` : `knowledge changed before restore: expected ${candidateRef.candidateHash}, got ${currentHash}`;
2565
- return await blockCandidateTransition(input, candidate, target, reason);
2710
+ const mutation2 = Object.freeze({
2711
+ target,
2712
+ beforeHash: currentHash,
2713
+ afterHash: currentHash,
2714
+ changed: false,
2715
+ transactionId: null,
2716
+ recovered: false
2717
+ });
2718
+ const activationResult2 = input.activation ? await persistKnowledgeActivationResult(
2719
+ runDir,
2720
+ candidateRef,
2721
+ input.activation,
2722
+ target,
2723
+ mutation2
2724
+ ) : void 0;
2725
+ const blocked = await blockCandidateTransition(
2726
+ input,
2727
+ candidate,
2728
+ target,
2729
+ reason,
2730
+ currentHash
2731
+ );
2732
+ return activationResult2 ? { ...blocked, activationResult: activationResult2 } : blocked;
2566
2733
  }
2567
- if (currentHash !== desiredHash) {
2734
+ if (currentHash !== desiredHash && !pending) {
2568
2735
  pending = await withMeasuredCandidateSnapshot(
2569
2736
  input.root,
2570
2737
  runDir,
@@ -2579,6 +2746,7 @@ async function applyKnowledgeCandidateTarget(input, target) {
2579
2746
  root: input.root,
2580
2747
  transactionRoot,
2581
2748
  purpose,
2749
+ recoveryOwner,
2582
2750
  mutations: await knowledgePlanMutations(targetRoot, plan),
2583
2751
  includeUnchanged: true,
2584
2752
  now: input.now
@@ -2588,6 +2756,7 @@ async function applyKnowledgeCandidateTarget(input, target) {
2588
2756
  if (!pending) {
2589
2757
  throw new Error(`knowledge ${action} plan unexpectedly contained no file changes`);
2590
2758
  }
2759
+ transactionId = pending.transactionId;
2591
2760
  try {
2592
2761
  assertCandidateTransitionTransaction(pending, candidateRef, target);
2593
2762
  } catch (error) {
@@ -2596,19 +2765,13 @@ async function applyKnowledgeCandidateTarget(input, target) {
2596
2765
  root: input.root,
2597
2766
  transactionRoot,
2598
2767
  transaction: pending,
2599
- beforeCommit() {
2600
- mutationLock.assertOwned();
2601
- input.assertRunOwned();
2602
- }
2768
+ beforeCommit: assertOwned
2603
2769
  });
2604
2770
  await finishKnowledgeFileTransaction({
2605
2771
  root: input.root,
2606
2772
  transactionRoot,
2607
2773
  transaction: pending,
2608
- assertOwned() {
2609
- mutationLock.assertOwned();
2610
- input.assertRunOwned();
2611
- }
2774
+ assertOwned
2612
2775
  });
2613
2776
  } catch (cleanupError) {
2614
2777
  throw new AggregateError(
@@ -2625,23 +2788,19 @@ async function applyKnowledgeCandidateTarget(input, target) {
2625
2788
  root: input.root,
2626
2789
  transactionRoot,
2627
2790
  transaction: pending,
2628
- beforeCommit() {
2629
- mutationLock.assertOwned();
2630
- input.assertRunOwned();
2631
- }
2791
+ beforeCommit: assertOwned
2632
2792
  });
2633
2793
  }
2634
- mutationLock.assertOwned();
2635
- input.assertRunOwned();
2794
+ assertOwned();
2636
2795
  if (await hashKnowledgeBase(input.root) !== desiredHash) {
2637
2796
  throw new Error(`knowledge ${action} content does not match the approved target`);
2638
2797
  }
2639
2798
  await writeKnowledgeIndex(input.root);
2640
2799
  } catch (error) {
2641
2800
  if (!pending) throw error;
2801
+ if (recovered && input.activation) throw error;
2642
2802
  try {
2643
- mutationLock.assertOwned();
2644
- input.assertRunOwned();
2803
+ assertOwned();
2645
2804
  } catch (ownershipError) {
2646
2805
  throw new AggregateError(
2647
2806
  [error, ownershipError],
@@ -2653,19 +2812,13 @@ async function applyKnowledgeCandidateTarget(input, target) {
2653
2812
  root: input.root,
2654
2813
  transactionRoot,
2655
2814
  transaction: pending,
2656
- beforeCommit() {
2657
- mutationLock.assertOwned();
2658
- input.assertRunOwned();
2659
- }
2815
+ beforeCommit: assertOwned
2660
2816
  });
2661
2817
  await finishKnowledgeFileTransaction({
2662
2818
  root: input.root,
2663
2819
  transactionRoot,
2664
2820
  transaction: pending,
2665
- assertOwned() {
2666
- mutationLock.assertOwned();
2667
- input.assertRunOwned();
2668
- }
2821
+ assertOwned
2669
2822
  });
2670
2823
  await writeKnowledgeIndex(input.root);
2671
2824
  } catch (rollbackError) {
@@ -2685,29 +2838,64 @@ async function applyKnowledgeCandidateTarget(input, target) {
2685
2838
  state.updatedAt = input.now().toISOString();
2686
2839
  await saveState(runDir, state, input.onState);
2687
2840
  await ensureCandidateTransitionEvent(runDir, candidateRef, target);
2841
+ let finalHash = await hashKnowledgeBase(input.root);
2842
+ if (finalHash !== desiredHash) {
2843
+ throw new Error(`knowledge ${action} changed before its result was returned`);
2844
+ }
2845
+ const mutation = Object.freeze({
2846
+ target,
2847
+ beforeHash: transactionId ? sourceKnowledgeHash(candidateRef, target) : currentHash,
2848
+ afterHash: finalHash,
2849
+ changed: transactionId !== null,
2850
+ transactionId,
2851
+ recovered: recovered !== void 0
2852
+ });
2853
+ const activationResult = input.activation ? await persistKnowledgeActivationResult(
2854
+ runDir,
2855
+ candidateRef,
2856
+ input.activation,
2857
+ target,
2858
+ mutation
2859
+ ) : void 0;
2860
+ if (await hashKnowledgeBase(input.root) !== finalHash) {
2861
+ throw new Error(`knowledge ${action} changed while its result was persisted`);
2862
+ }
2688
2863
  if (pending) {
2689
2864
  await finishKnowledgeFileTransaction({
2690
2865
  root: input.root,
2691
2866
  transactionRoot,
2692
2867
  transaction: pending,
2693
- assertOwned() {
2694
- mutationLock.assertOwned();
2695
- input.assertRunOwned();
2696
- }
2868
+ assertOwned
2697
2869
  });
2698
2870
  }
2699
- return candidateTransitionResult(input, candidate, target === "candidate", false);
2871
+ finalHash = await hashKnowledgeBase(input.root);
2872
+ if (finalHash !== desiredHash) {
2873
+ throw new Error(`knowledge ${action} changed before its result was returned`);
2874
+ }
2875
+ return candidateTransitionResult(
2876
+ input,
2877
+ candidate,
2878
+ target === "candidate",
2879
+ false,
2880
+ mutation,
2881
+ activationResult
2882
+ );
2700
2883
  },
2701
2884
  {
2702
2885
  staleMs: input.leaseTtlMs,
2703
2886
  resumeTransaction: {
2704
2887
  purpose,
2705
- validate: (transaction) => assertCandidateTransitionTransaction(transaction, candidateRef, target)
2888
+ recoveryOwner,
2889
+ validate: (transaction) => assertCandidateTransitionTransaction(transaction, candidateRef, target),
2890
+ deferFinish: input.activation !== void 0
2706
2891
  }
2707
2892
  }
2708
2893
  );
2709
2894
  }
2710
- async function blockCandidateTransition(input, candidate, target, reason) {
2895
+ function candidateTransitionConflictReason(target, candidate, currentHash) {
2896
+ return target === "candidate" ? `base changed before promotion: expected ${candidate.baseHash}, got ${currentHash}` : `knowledge changed before restore: expected ${candidate.candidateHash}, got ${currentHash}`;
2897
+ }
2898
+ async function blockCandidateTransition(input, candidate, target, reason, currentHash) {
2711
2899
  if (input.state.status === "promoted") {
2712
2900
  candidate.status = "blocked";
2713
2901
  candidate.updatedAt = input.now().toISOString();
@@ -2720,19 +2908,194 @@ async function blockCandidateTransition(input, candidate, target, reason) {
2720
2908
  candidateId: candidate.candidateId,
2721
2909
  reason
2722
2910
  });
2723
- return candidateTransitionResult(input, candidate, false, true);
2911
+ return candidateTransitionResult(input, candidate, false, true, {
2912
+ target,
2913
+ beforeHash: currentHash,
2914
+ afterHash: currentHash,
2915
+ changed: false,
2916
+ transactionId: null,
2917
+ recovered: false
2918
+ });
2724
2919
  }
2725
- function candidateTransitionResult(input, candidate, promoted, blocked) {
2920
+ function candidateTransitionResult(input, candidate, promoted, blocked, mutation, activationResult) {
2726
2921
  return {
2727
2922
  runId: input.candidateRef.runId,
2728
2923
  state: input.state,
2729
2924
  candidate,
2730
2925
  ...input.lifecycle ? { lifecycle: input.lifecycle } : {},
2926
+ mutation,
2927
+ ...activationResult ? { activationResult } : {},
2731
2928
  promoted,
2732
2929
  blocked
2733
2930
  };
2734
2931
  }
2932
+ function sourceKnowledgeHash(candidate, target) {
2933
+ return target === "candidate" ? candidate.baseHash : candidate.candidateHash;
2934
+ }
2935
+ function targetForKnowledgeActivation(activation) {
2936
+ return activation.intent === "activate-candidate" ? "candidate" : "baseline";
2937
+ }
2938
+ function resolveKnowledgeActivationPersistence(input, candidate, target) {
2939
+ const activation = verifyCanonicalKnowledgeActivation(input.activation);
2940
+ assertKnowledgeActivationAuthority(activation, candidate, target, input.identity);
2941
+ const attemptedAt = z.iso.datetime().parse(input.attemptedAt);
2942
+ if (Date.parse(attemptedAt) < Date.parse(activation.authorizedAt) || Date.parse(attemptedAt) >= Date.parse(activation.expiresAt)) {
2943
+ throw new Error("knowledge activation attempt is outside its authorization window");
2944
+ }
2945
+ return Object.freeze({
2946
+ activation,
2947
+ attemptedAt,
2948
+ identity: input.identity,
2949
+ createResult: input.createResult
2950
+ });
2951
+ }
2952
+ function assertKnowledgeActivationAuthority(activation, candidate, target, identity) {
2953
+ if (!identity.trim()) throw new Error("knowledge activation identity is required");
2954
+ if (targetForKnowledgeActivation(activation) !== target) {
2955
+ throw new Error("knowledge activation intent does not match the requested transition");
2956
+ }
2957
+ if (activation.targets.length !== 1) {
2958
+ throw new Error("knowledge activation requires exactly one target");
2959
+ }
2960
+ const authorizedTarget = activation.targets[0];
2961
+ if (authorizedTarget.surface !== "knowledge" || authorizedTarget.identity !== identity) {
2962
+ throw new Error("knowledge activation target does not match this knowledge base");
2963
+ }
2964
+ if (authorizedTarget.expectedBaseDigest !== prefixedKnowledgeDigest(sourceKnowledgeHash(candidate, target))) {
2965
+ throw new Error("knowledge activation does not authorize the measured source state");
2966
+ }
2967
+ }
2968
+ function verifyCanonicalKnowledgeActivation(value) {
2969
+ const activation = agentImprovementActivationSchema.parse(value);
2970
+ if (canonicalCandidateDigest(omitTopLevelDigest(activation)) !== activation.digest) {
2971
+ throw new Error("knowledge activation digest does not match its canonical content");
2972
+ }
2973
+ return immutableJsonValue(structuredClone(activation));
2974
+ }
2975
+ function verifyCanonicalKnowledgeActivationResult(value) {
2976
+ const result = agentImprovementActivationResultSchema.parse(value);
2977
+ if (canonicalCandidateDigest(omitTopLevelDigest(result)) !== result.digest) {
2978
+ throw new Error("knowledge activation result digest does not match its canonical content");
2979
+ }
2980
+ return immutableJsonValue(structuredClone(result));
2981
+ }
2982
+ async function persistKnowledgeActivationResult(runDir, candidate, persistence, target, mutation) {
2983
+ const result = assertKnowledgeActivationResult(
2984
+ persistence.activation,
2985
+ candidate,
2986
+ target,
2987
+ persistence.identity,
2988
+ mutation,
2989
+ await persistence.createResult(Object.freeze({ ...mutation })),
2990
+ persistence.attemptedAt
2991
+ );
2992
+ const record = knowledgeImprovementActivationRecordSchema.parse({
2993
+ kind: "knowledge-improvement-activation-result",
2994
+ candidateId: candidate.candidateId,
2995
+ mutation,
2996
+ result
2997
+ });
2998
+ const existing = await loadKnowledgeActivationRecord(
2999
+ runDir,
3000
+ candidate,
3001
+ persistence.activation,
3002
+ target,
3003
+ persistence.identity
3004
+ );
3005
+ if (existing) {
3006
+ if (canonicalJson(existing) !== canonicalJson(record)) {
3007
+ throw new Error("knowledge activation result identity conflicts with durable content");
3008
+ }
3009
+ return existing.result;
3010
+ }
3011
+ await writeJsonDurableWithinRoot(
3012
+ runDir,
3013
+ knowledgeActivationResultPath(persistence.activation.digest),
3014
+ record
3015
+ );
3016
+ const stored = await loadKnowledgeActivationRecord(
3017
+ runDir,
3018
+ candidate,
3019
+ persistence.activation,
3020
+ target,
3021
+ persistence.identity
3022
+ );
3023
+ if (!stored || canonicalJson(stored) !== canonicalJson(record)) {
3024
+ throw new Error("knowledge activation result was not durably persisted");
3025
+ }
3026
+ return stored.result;
3027
+ }
3028
+ async function loadKnowledgeActivationRecord(runDir, candidate, activation, target, identity) {
3029
+ let raw;
3030
+ try {
3031
+ const file = await readRegularFileWithinRoot(
3032
+ runDir,
3033
+ knowledgeActivationResultPath(activation.digest)
3034
+ );
3035
+ raw = JSON.parse(file.bytes.toString("utf8"));
3036
+ } catch (error) {
3037
+ if (isMissingFile(error)) return null;
3038
+ throw error;
3039
+ }
3040
+ const record = knowledgeImprovementActivationRecordSchema.parse(raw);
3041
+ if (record.candidateId !== candidate.candidateId) {
3042
+ throw new Error("knowledge activation result belongs to another candidate");
3043
+ }
3044
+ const result = assertKnowledgeActivationResult(
3045
+ activation,
3046
+ candidate,
3047
+ target,
3048
+ identity,
3049
+ record.mutation,
3050
+ record.result
3051
+ );
3052
+ return immutableJsonValue({ ...record, result });
3053
+ }
3054
+ function assertKnowledgeActivationResult(activation, candidate, target, identity, mutationInput, resultInput, attemptedAt) {
3055
+ assertKnowledgeActivationAuthority(activation, candidate, target, identity);
3056
+ const mutation = knowledgeImprovementMutationReceiptSchema.parse(mutationInput);
3057
+ const result = verifyCanonicalKnowledgeActivationResult(resultInput);
3058
+ if (result.idempotencyKey !== activation.digest || attemptedAt !== void 0 && result.attemptedAt !== attemptedAt || Date.parse(result.attemptedAt) < Date.parse(activation.authorizedAt) || Date.parse(result.attemptedAt) >= Date.parse(activation.expiresAt) || mutation.target !== target || mutation.changed !== (mutation.beforeHash !== mutation.afterHash) || !mutation.changed && (mutation.transactionId !== null || mutation.recovered)) {
3059
+ throw new Error("knowledge activation result does not bind its authorized mutation");
3060
+ }
3061
+ const sourceDigest = prefixedKnowledgeDigest(sourceKnowledgeHash(candidate, target));
3062
+ const desiredDigest = prefixedKnowledgeDigest(
3063
+ target === "candidate" ? candidate.candidateHash : candidate.baseHash
3064
+ );
3065
+ const beforeDigest = prefixedKnowledgeDigest(mutation.beforeHash);
3066
+ const afterDigest = prefixedKnowledgeDigest(mutation.afterHash);
3067
+ const outcome = result.outcome;
3068
+ if (mutation.changed) {
3069
+ if (mutation.transactionId === null || beforeDigest !== sourceDigest || afterDigest !== desiredDigest || outcome.status !== "applied" || outcome.transactionId !== mutation.transactionId || outcome.targets.length !== 1 || outcome.targets[0]?.surface !== "knowledge" || outcome.targets[0]?.identity !== identity || outcome.targets[0]?.beforeDigest !== beforeDigest || outcome.targets[0]?.afterDigest !== afterDigest) {
3070
+ throw new Error("knowledge activation result does not prove the applied transaction");
3071
+ }
3072
+ return result;
3073
+ }
3074
+ const expectedStatus = afterDigest === desiredDigest ? "already-applied" : "conflict";
3075
+ if (afterDigest === sourceDigest || outcome.status !== expectedStatus || outcome.targets.length !== 1 || outcome.targets[0]?.surface !== "knowledge" || outcome.targets[0]?.identity !== identity || outcome.targets[0]?.currentDigest !== afterDigest) {
3076
+ throw new Error("knowledge activation result does not prove the observed target state");
3077
+ }
3078
+ return result;
3079
+ }
3080
+ function knowledgeActivationResultPath(digest) {
3081
+ const parsed = sha256DigestSchema.parse(digest);
3082
+ return `activation-results/${parsed.slice("sha256:".length)}.json`;
3083
+ }
3084
+ function prefixedKnowledgeDigest(hash) {
3085
+ return sha256DigestSchema.parse(`sha256:${digestSchema.parse(hash)}`);
3086
+ }
3087
+ function immutableJsonValue(value) {
3088
+ if (value === null || typeof value !== "object") return value;
3089
+ for (const child of Object.values(value)) immutableJsonValue(child);
3090
+ return Object.freeze(value);
3091
+ }
2735
3092
  function candidateRefFor(runId, state, candidate) {
3093
+ if (candidate.status !== "candidate-ready" && candidate.status !== "promoted") {
3094
+ throw new Error(`knowledge candidate '${candidate.candidateId}' is not ready`);
3095
+ }
3096
+ return candidateIdentityFor(runId, state, candidate);
3097
+ }
3098
+ function candidateIdentityFor(runId, state, candidate) {
2736
3099
  if (!candidate.candidateHash) {
2737
3100
  throw new Error(`knowledge candidate '${candidate.candidateId}' has no content hash`);
2738
3101
  }
@@ -2742,9 +3105,6 @@ function candidateRefFor(runId, state, candidate) {
2742
3105
  if (!candidate.promotionPlanHash) {
2743
3106
  throw new Error(`knowledge candidate '${candidate.candidateId}' has no promotion plan hash`);
2744
3107
  }
2745
- if (candidate.status !== "candidate-ready" && candidate.status !== "promoted") {
2746
- throw new Error(`knowledge candidate '${candidate.candidateId}' is not ready`);
2747
- }
2748
3108
  return Object.freeze({
2749
3109
  kind: "knowledge-improvement-candidate",
2750
3110
  runId,
@@ -2784,17 +3144,17 @@ async function withMeasuredCandidateSnapshot(liveRoot, runDir, state, candidateR
2784
3144
  return result;
2785
3145
  });
2786
3146
  }
2787
- async function withIsolatedCandidateCopy(sourceRoot, expectedHash, use) {
2788
- const isolationRoot = await mkdtemp(join(tmpdir(), "agent-knowledge-candidate-"));
2789
- const candidateRoot = join(isolationRoot, "candidate");
3147
+ async function withIsolatedKnowledgeCopy(sourceRoot, expectedHash, target, use) {
3148
+ const isolationRoot = await mkdtemp(join(tmpdir(), "agent-knowledge-snapshot-"));
3149
+ const snapshotRoot = join(isolationRoot, "snapshot");
2790
3150
  try {
2791
- await copyKnowledgeWorkspace(sourceRoot, candidateRoot);
2792
- if (await hashKnowledgeBase(candidateRoot) !== expectedHash) {
2793
- throw new Error("isolated knowledge candidate does not match its approved content");
3151
+ await copyKnowledgeWorkspace(sourceRoot, snapshotRoot);
3152
+ if (await hashKnowledgeBase(snapshotRoot) !== expectedHash) {
3153
+ throw new Error(`isolated knowledge ${target} does not match its measured content`);
2794
3154
  }
2795
- const result = await use(candidateRoot);
2796
- if (await hashKnowledgeBase(candidateRoot) !== expectedHash) {
2797
- throw new Error("knowledge candidate snapshot changed during use");
3155
+ const result = await use(snapshotRoot);
3156
+ if (await hashKnowledgeBase(snapshotRoot) !== expectedHash) {
3157
+ throw new Error(`knowledge ${target} snapshot changed during use`);
2798
3158
  }
2799
3159
  return result;
2800
3160
  } finally {
@@ -3534,10 +3894,10 @@ function fromAgentCandidateKnowledgeRef(candidate) {
3534
3894
  });
3535
3895
  }
3536
3896
  function prefixedDigest(value) {
3537
- return sha256DigestSchema.parse(`sha256:${value}`);
3897
+ return sha256DigestSchema2.parse(`sha256:${value}`);
3538
3898
  }
3539
3899
  function rawDigest(value) {
3540
- return sha256DigestSchema.parse(value).slice("sha256:".length);
3900
+ return sha256DigestSchema2.parse(value).slice("sha256:".length);
3541
3901
  }
3542
3902
 
3543
3903
  // src/changes.ts
@@ -5966,6 +6326,7 @@ export {
5966
6326
  layoutFor,
5967
6327
  lensDistribution,
5968
6328
  lintKnowledgeIndex,
6329
+ loadKnowledgeImprovementActivationResult,
5969
6330
  loadKnowledgeImprovementEvents,
5970
6331
  loadKnowledgeImprovementState,
5971
6332
  loadKnowledgePages,
@@ -6036,6 +6397,7 @@ export {
6036
6397
  validateKnowledgeIndex,
6037
6398
  withCitedClaim,
6038
6399
  withKnowledgeImprovementCandidate,
6400
+ withKnowledgeImprovementComparison,
6039
6401
  writeJson,
6040
6402
  writeKnowledgeIndex,
6041
6403
  writeSourceRegistry