opencode-swarm 7.161.1 → 7.162.0

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.
Files changed (75) hide show
  1. package/dist/cli/{coder-settlement-2jteb5vy.js → coder-settlement-s8cskxk7.js} +8 -8
  2. package/dist/cli/{config-doctor-1yn317qf.js → config-doctor-636h188h.js} +2 -2
  3. package/dist/cli/{core-k687jrr3.js → core-h3hsczyq.js} +1 -1
  4. package/dist/cli/{curation-policy-ej9xc0cd.js → curation-policy-ayq5kmnf.js} +4 -4
  5. package/dist/cli/{curator-llm-factory-m1ke17eq.js → curator-llm-factory-g65xmh8f.js} +30 -30
  6. package/dist/cli/{curator-q017rjfk.js → curator-p0dwpn2x.js} +30 -30
  7. package/dist/cli/{evidence-summary-service-6jzfa547.js → evidence-summary-service-hvbqyb4g.js} +12 -12
  8. package/dist/cli/{gate-evidence-j6nskaka.js → gate-evidence-dhgmnsh0.js} +4 -4
  9. package/dist/cli/{guardrail-explain-xw3ma90f.js → guardrail-explain-s0dzn8c6.js} +31 -31
  10. package/dist/cli/{guardrail-log-etmzhbge.js → guardrail-log-qc7rfhv2.js} +6 -6
  11. package/dist/cli/{guardrail-reset-z9fkxksy.js → guardrail-reset-cxkf5h0x.js} +30 -30
  12. package/dist/cli/{hive-promoter-dmwjya83.js → hive-promoter-ppbrv8m1.js} +30 -30
  13. package/dist/cli/{index-zhdfqz89.js → index-0h63swrc.js} +5 -5
  14. package/dist/cli/{index-1j532307.js → index-1mbwgw4p.js} +2 -2
  15. package/dist/cli/{index-9kp3cp5n.js → index-1wapt173.js} +3 -3
  16. package/dist/cli/{index-e0re84cx.js → index-25xvw7hq.js} +32 -32
  17. package/dist/cli/{index-cs9w1hfs.js → index-2rhs7j4r.js} +606 -226
  18. package/dist/cli/{index-rracnf2y.js → index-2ze7yqm8.js} +1 -1
  19. package/dist/cli/{index-88wb0z9r.js → index-3gwkmq53.js} +2 -2
  20. package/dist/cli/{index-cbcfpgz9.js → index-4wmpv2kn.js} +6 -6
  21. package/dist/cli/{index-jm21vbyp.js → index-5wq0dd30.js} +2 -2
  22. package/dist/cli/{index-c1geyan9.js → index-6w468bjy.js} +1 -1
  23. package/dist/cli/{index-nm0cpfrv.js → index-77syzssw.js} +4 -4
  24. package/dist/cli/{index-c6zyqgpq.js → index-7z9gyts0.js} +1 -1
  25. package/dist/cli/{index-k55td019.js → index-80pmq6q2.js} +1 -1
  26. package/dist/cli/{index-7d79mj06.js → index-8vwe2qjh.js} +6 -6
  27. package/dist/cli/{index-awjx0wgk.js → index-9wrmzsnk.js} +1 -1
  28. package/dist/cli/{index-s33ee2w3.js → index-acnwj4pc.js} +1 -1
  29. package/dist/cli/{index-y14mwbk8.js → index-bdmg6yvj.js} +2 -2
  30. package/dist/cli/{index-d8st72j7.js → index-db520vnn.js} +1 -1
  31. package/dist/cli/{index-dq41vsf9.js → index-jw7z3gem.js} +1 -1
  32. package/dist/cli/{index-7619tk9a.js → index-kk9yk7an.js} +1 -1
  33. package/dist/cli/{index-4p6x3xxy.js → index-mfewnd7j.js} +4 -4
  34. package/dist/cli/{index-134d35c1.js → index-n249fbw7.js} +9 -0
  35. package/dist/cli/{index-hq57ytm5.js → index-pde4zn7j.js} +1 -1
  36. package/dist/cli/{index-dh5q6z7y.js → index-q87ht62k.js} +2 -2
  37. package/dist/cli/{index-k4xey4kh.js → index-qn4w0gar.js} +1 -1
  38. package/dist/cli/{index-hvfsz7e3.js → index-qw893zqp.js} +6 -6
  39. package/dist/cli/{index-qd9mrd7e.js → index-r7rn6e5f.js} +5 -5
  40. package/dist/cli/{index-yhadrxka.js → index-sjkxrf06.js} +4 -4
  41. package/dist/cli/{index-29f9te70.js → index-tjaawnws.js} +2 -2
  42. package/dist/cli/{index-epjvntnm.js → index-tnj464kq.js} +1 -1
  43. package/dist/cli/{index-32avgbyq.js → index-vcv9m2q5.js} +2 -2
  44. package/dist/cli/{index-za42r41w.js → index-vkaqrrsc.js} +2 -2
  45. package/dist/cli/{index-f5axf6vq.js → index-ydbd41sv.js} +1 -1
  46. package/dist/cli/{index-bn78ywnb.js → index-z4n60b0a.js} +1 -1
  47. package/dist/cli/index.js +30 -30
  48. package/dist/cli/{knowledge-escalator-cqyxfjfh.js → knowledge-escalator-nyeg4ndw.js} +10 -10
  49. package/dist/cli/{knowledge-events-g707awjf.js → knowledge-events-z7smbnff.js} +8 -8
  50. package/dist/cli/{knowledge-link-8knefv8x.js → knowledge-link-0byjmj0w.js} +3 -3
  51. package/dist/cli/{knowledge-store-byhv66xq.js → knowledge-store-p14t72y5.js} +4 -4
  52. package/dist/cli/{knowledge-validator-qfkr8eje.js → knowledge-validator-jg6zhx9s.js} +6 -6
  53. package/dist/cli/{pending-delegations-n3baz9m3.js → pending-delegations-0111ywd2.js} +3 -3
  54. package/dist/cli/{pr-review-reentry-authorization-ttctn8xv.js → pr-review-reentry-authorization-b7ceja9y.js} +30 -30
  55. package/dist/cli/{pr-subscriptions-3zwthnan.js → pr-subscriptions-4e64sbwg.js} +3 -3
  56. package/dist/cli/{runner-fzgkd6m1.js → runner-rddddxmq.js} +4 -4
  57. package/dist/cli/{scan-cursor-fbtgyeft.js → scan-cursor-wjad4yd7.js} +5 -5
  58. package/dist/cli/{schema-xb7d31wq.js → schema-9ebqj7mv.js} +1 -1
  59. package/dist/cli/{scope-persistence-5npzdfjf.js → scope-persistence-j5hz4mnd.js} +7 -7
  60. package/dist/cli/{skill-generator-k1e7nee3.js → skill-generator-x6b05jz6.js} +12 -12
  61. package/dist/cli/{sqlite-loader-r1kgt2pq.js → sqlite-loader-c92413n7.js} +1 -1
  62. package/dist/cli/{worktree-collision-ownership-jfam09nj.js → worktree-collision-ownership-g5n5xbqy.js} +4 -4
  63. package/dist/cli/{worktree-isolation-avp3yxcs.js → worktree-isolation-ha70bwes.js} +30 -30
  64. package/dist/db/sqlite-loader.d.ts +1 -0
  65. package/dist/index.js +32 -18
  66. package/dist/memory/curator-decision-helpers.d.ts +1 -0
  67. package/dist/memory/index.d.ts +1 -1
  68. package/dist/memory/local-jsonl-provider.d.ts +2 -0
  69. package/dist/memory/proposal-load.d.ts +15 -0
  70. package/dist/memory/relation-constants.d.ts +3 -0
  71. package/dist/memory/relations.d.ts +8 -0
  72. package/dist/memory/schema.d.ts +8 -0
  73. package/dist/memory/sqlite-provider.d.ts +3 -0
  74. package/dist/memory/types.d.ts +21 -0
  75. package/package.json +1 -1
@@ -6,7 +6,7 @@ import {
6
6
  resolveExecutableFromPath,
7
7
  runEvaluation,
8
8
  runExternalTool
9
- } from "./index-s33ee2w3.js";
9
+ } from "./index-acnwj4pc.js";
10
10
  import {
11
11
  finalizeShellAuditForClose,
12
12
  isPolicyProtectedPath,
@@ -15,13 +15,13 @@ import {
15
15
  protectedRoots,
16
16
  redactDecisionLineForArchive,
17
17
  stableCanonicalStringify
18
- } from "./index-29f9te70.js";
18
+ } from "./index-tjaawnws.js";
19
19
  import {
20
20
  detectStraySwarmDirs,
21
21
  readDoctorArtifact,
22
22
  removeStraySwarmDir,
23
23
  runConfigDoctor
24
- } from "./index-bn78ywnb.js";
24
+ } from "./index-z4n60b0a.js";
25
25
  import {
26
26
  MAX_TRANSIENT_RETRIES,
27
27
  archiveEvidence,
@@ -68,7 +68,7 @@ import {
68
68
  validateProjectRoot,
69
69
  validateSymlinkBoundary,
70
70
  validateTargetWithinRoot
71
- } from "./index-7d79mj06.js";
71
+ } from "./index-8vwe2qjh.js";
72
72
  import {
73
73
  EvaluationTaskV1Schema,
74
74
  GateAuditManifestV1Schema,
@@ -85,7 +85,7 @@ import {
85
85
  saveGateAuditResult,
86
86
  savePromotionDecision,
87
87
  saveTaskSetSnapshot
88
- } from "./index-dq41vsf9.js";
88
+ } from "./index-jw7z3gem.js";
89
89
  import {
90
90
  canonicalHash,
91
91
  canonicalJson,
@@ -99,7 +99,7 @@ import {
99
99
  } from "./index-txdagbh7.js";
100
100
  import {
101
101
  loadDatabaseCtor
102
- } from "./index-134d35c1.js";
102
+ } from "./index-n249fbw7.js";
103
103
  import {
104
104
  LANGUAGE_REGISTRY,
105
105
  _internals as _internals3,
@@ -130,15 +130,15 @@ import {
130
130
  retireSkill,
131
131
  selectCandidateEntries,
132
132
  syncBundledProjectSkillsIfMissingAsync
133
- } from "./index-yhadrxka.js";
133
+ } from "./index-sjkxrf06.js";
134
134
  import {
135
135
  readRecentEscalations
136
- } from "./index-zhdfqz89.js";
136
+ } from "./index-0h63swrc.js";
137
137
  import {
138
138
  alreadyCuratedThisGeneration,
139
139
  claimNextScanBatch,
140
140
  resolveCohortId
141
- } from "./index-88wb0z9r.js";
141
+ } from "./index-3gwkmq53.js";
142
142
  import {
143
143
  CURATOR_INIT_PROMPT,
144
144
  CURATOR_PHASE_PROMPT,
@@ -162,7 +162,7 @@ import {
162
162
  listActive,
163
163
  subscribe,
164
164
  unsubscribe
165
- } from "./index-y14mwbk8.js";
165
+ } from "./index-bdmg6yvj.js";
166
166
  import {
167
167
  clearWorktreeMergeStatus,
168
168
  getAllWorktreeMergeFailures,
@@ -174,7 +174,7 @@ import {
174
174
  listCoderSettlementWalStates,
175
175
  recoverCoderSettlement,
176
176
  recoverStaleCoderSettlements
177
- } from "./index-qd9mrd7e.js";
177
+ } from "./index-r7rn6e5f.js";
178
178
  import {
179
179
  parsePorcelainV2Snapshot,
180
180
  resolveCommitCountSince,
@@ -207,7 +207,7 @@ import {
207
207
  WORKTREE_LIFECYCLE_LOCK_FILE,
208
208
  recordWorktreeProvisioningOwner,
209
209
  removeWorktreeProvisioningOwner
210
- } from "./index-awjx0wgk.js";
210
+ } from "./index-9wrmzsnk.js";
211
211
  import {
212
212
  appendUnactionable,
213
213
  quarantineEntry,
@@ -218,11 +218,11 @@ import {
218
218
  validateActionableFields,
219
219
  validateLesson,
220
220
  validateSkillPath
221
- } from "./index-nm0cpfrv.js";
221
+ } from "./index-77syzssw.js";
222
222
  import {
223
223
  authorizeCuration,
224
224
  buildConfigFingerprintInput
225
- } from "./index-f5axf6vq.js";
225
+ } from "./index-ydbd41sv.js";
226
226
  import {
227
227
  cohortConfigFingerprint
228
228
  } from "./index-rtry5xyf.js";
@@ -253,12 +253,12 @@ import {
253
253
  transitionTaskWorkflowEvidence,
254
254
  withTaskEvidenceTransaction,
255
255
  writeWorkflowWalFile
256
- } from "./index-32avgbyq.js";
256
+ } from "./index-vcv9m2q5.js";
257
257
  import {
258
258
  makeWorktreeBranchName,
259
259
  provisionWorktree,
260
260
  removeWorktree
261
- } from "./index-c1geyan9.js";
261
+ } from "./index-6w468bjy.js";
262
262
  import {
263
263
  DEFAULT_STALE_DELEGATION_TIMEOUT_MS,
264
264
  DELEGATION_COMPACTION_HIGH_WATER_BYTES,
@@ -286,7 +286,7 @@ import {
286
286
  scanBackgroundCoderReservationsForAdmission,
287
287
  scanDelegationsForRecovery,
288
288
  sweepStaleDelegations
289
- } from "./index-za42r41w.js";
289
+ } from "./index-vkaqrrsc.js";
290
290
  import {
291
291
  compositeSessionKey,
292
292
  sessionKeySuffix
@@ -297,7 +297,7 @@ import {
297
297
  clearAllScopes,
298
298
  clearScopeBindingFromDisk,
299
299
  clearScopeBindings
300
- } from "./index-4p6x3xxy.js";
300
+ } from "./index-mfewnd7j.js";
301
301
  import {
302
302
  appendLedgerEvent,
303
303
  computePlanHash,
@@ -312,7 +312,7 @@ import {
312
312
  readPlanEpochIdentity,
313
313
  replayFromLedgerWithStatus,
314
314
  takeSnapshotEvent
315
- } from "./index-hq57ytm5.js";
315
+ } from "./index-pde4zn7j.js";
316
316
  import {
317
317
  CORE_EVENT_LOCKED,
318
318
  appendCoreEventSync,
@@ -322,7 +322,7 @@ import {
322
322
  hasSpecDriftAuditEvent,
323
323
  hasTaskRepairAuditEvent,
324
324
  readCoreEvents
325
- } from "./index-7619tk9a.js";
325
+ } from "./index-kk9yk7an.js";
326
326
  import {
327
327
  sanitizeDiagnosticText
328
328
  } from "./index-556ja8an.js";
@@ -371,7 +371,7 @@ import {
371
371
  transactFile,
372
372
  transactKnowledge,
373
373
  wordBigrams
374
- } from "./index-cbcfpgz9.js";
374
+ } from "./index-4wmpv2kn.js";
375
375
  import {
376
376
  MAX_EVENT_LOG_ENTRIES,
377
377
  _internals as _internals2,
@@ -387,10 +387,10 @@ import {
387
387
  recordKnowledgeEvent,
388
388
  recordPhaseCloseIntent,
389
389
  resolveKnowledgeEventsPath
390
- } from "./index-hvfsz7e3.js";
390
+ } from "./index-qw893zqp.js";
391
391
  import {
392
392
  resolveWorktreeIsolationConfig
393
- } from "./index-jm21vbyp.js";
393
+ } from "./index-5wq0dd30.js";
394
394
  import {
395
395
  RetrospectiveEvidenceSchema
396
396
  } from "./index-zg9d392c.js";
@@ -413,7 +413,7 @@ import {
413
413
  init_loader,
414
414
  loadPluginConfig,
415
415
  loadPluginConfigWithMeta
416
- } from "./index-epjvntnm.js";
416
+ } from "./index-tnj464kq.js";
417
417
  import {
418
418
  DEFAULT_HARNESS_EVOLUTION_CONFIG,
419
419
  DEFAULT_PR_REVIEW_RESILIENCE_CONFIG,
@@ -429,7 +429,7 @@ import {
429
429
  package_default,
430
430
  resolveAutoReviewConfig,
431
431
  stripKnownSwarmPrefix
432
- } from "./index-rracnf2y.js";
432
+ } from "./index-2ze7yqm8.js";
433
433
  import {
434
434
  init_git_executable,
435
435
  resolveGitExecutable,
@@ -445,7 +445,7 @@ import {
445
445
  observeCuratorCompliance,
446
446
  observeStoreHealth,
447
447
  readLearningHealth
448
- } from "./index-k55td019.js";
448
+ } from "./index-80pmq6q2.js";
449
449
  import {
450
450
  AGENT_TOOL_MAP,
451
451
  ALL_AGENT_NAMES,
@@ -475,10 +475,10 @@ import {
475
475
  resolveLinkDir,
476
476
  sanitizeLinkId,
477
477
  writeLinkPointer
478
- } from "./index-c6zyqgpq.js";
478
+ } from "./index-7z9gyts0.js";
479
479
  import {
480
480
  atomicWriteFile
481
- } from "./index-d8st72j7.js";
481
+ } from "./index-db520vnn.js";
482
482
  import {
483
483
  KNOWLEDGE_SCHEMA_VERSION,
484
484
  OUTCOME_BLOCK_THRESHOLD,
@@ -498,7 +498,7 @@ import {
498
498
  atomicWriteSwarmFileSync,
499
499
  matchTempGrammar,
500
500
  parseTargetBasename
501
- } from "./index-dh5q6z7y.js";
501
+ } from "./index-q87ht62k.js";
502
502
  import {
503
503
  invalidateCachedArtifact,
504
504
  resetSwarmArtifactCache
@@ -21326,7 +21326,7 @@ function planMerges(active, threshold, maxComparisons, maxMerges) {
21326
21326
  };
21327
21327
  }
21328
21328
  async function sweepActiveNearDuplicates(directory, options = {}) {
21329
- const { loadPluginConfigWithMeta: loadPluginConfigWithMeta2 } = await import("./index-9kp3cp5n.js");
21329
+ const { loadPluginConfigWithMeta: loadPluginConfigWithMeta2 } = await import("./index-1wapt173.js");
21330
21330
  const { config } = loadPluginConfigWithMeta2(directory);
21331
21331
  const sweepConfig = config.learning?.dedup_sweep;
21332
21332
  if (sweepConfig?.enabled === false)
@@ -22463,7 +22463,7 @@ async function runCuratorPhase(directory, phase, agentsDispatched, config, knowl
22463
22463
  }
22464
22464
  if (config.skill_generation_enabled === true && skillCandidates.length > 0) {
22465
22465
  try {
22466
- const skillModule = await import("./skill-generator-k1e7nee3.js");
22466
+ const skillModule = await import("./skill-generator-x6b05jz6.js");
22467
22467
  for (const cand of skillCandidates) {
22468
22468
  if (cand.confidence < (config.min_skill_confidence ?? DEFAULT_SKILL_MIN_CONFIDENCE)) {
22469
22469
  continue;
@@ -22935,7 +22935,7 @@ async function applyCuratorKnowledgeUpdates(directory, recommendations, knowledg
22935
22935
  } catch {}
22936
22936
  }
22937
22937
  if (contradictedEntries.length > 0 && knowledgeConfig.contradiction_threshold_action === "quarantine") {
22938
- const { maybeQuarantineOnContradiction } = await import("./knowledge-escalator-cqyxfjfh.js");
22938
+ const { maybeQuarantineOnContradiction } = await import("./knowledge-escalator-nyeg4ndw.js");
22939
22939
  for (const { id } of contradictedEntries) {
22940
22940
  try {
22941
22941
  await maybeQuarantineOnContradiction(directory, id, knowledgeConfig.contradiction_quarantine_threshold, knowledgeConfig.contradiction_quarantine_window_days);
@@ -23754,7 +23754,7 @@ async function runCuratorPostMortem(directory, options = {}) {
23754
23754
  warnings.push("Failed to collect knowledge summary.");
23755
23755
  }
23756
23756
  try {
23757
- const { getScanStatus } = await import("./scan-cursor-fbtgyeft.js");
23757
+ const { getScanStatus } = await import("./scan-cursor-wjad4yd7.js");
23758
23758
  const scanStatus = await getScanStatus(directory);
23759
23759
  if (scanStatus.remaining_estimate > 0) {
23760
23760
  warnings.push(`Fair scan: reviewed ${knowledgeSummary.length} entries this sweep (generation ${scanStatus.generation}); ${scanStatus.remaining_estimate} more eligible entries will be visited in future curation sweeps.`);
@@ -23926,9 +23926,9 @@ var _internals27 = {
23926
23926
  verifyPostMortemKnowledgeActions,
23927
23927
  repairPostMortemActions,
23928
23928
  loadDefaultKnowledgeConfig: async (directory) => {
23929
- const { KnowledgeConfigSchema: KnowledgeConfigSchema2 } = await import("./schema-xb7d31wq.js");
23929
+ const { KnowledgeConfigSchema: KnowledgeConfigSchema2 } = await import("./schema-9ebqj7mv.js");
23930
23930
  try {
23931
- const { loadPluginConfigWithMeta: loadPluginConfigWithMeta2 } = await import("./index-9kp3cp5n.js");
23931
+ const { loadPluginConfigWithMeta: loadPluginConfigWithMeta2 } = await import("./index-1wapt173.js");
23932
23932
  const loaded = loadPluginConfigWithMeta2(directory);
23933
23933
  return KnowledgeConfigSchema2.parse(loaded.config.knowledge ?? {});
23934
23934
  } catch {
@@ -23936,11 +23936,11 @@ var _internals27 = {
23936
23936
  }
23937
23937
  },
23938
23938
  applyCuratorKnowledgeUpdates: async (directory, recommendations, knowledgeConfig, generation) => {
23939
- const { applyCuratorKnowledgeUpdates: applyCuratorKnowledgeUpdates2 } = await import("./curator-q017rjfk.js");
23939
+ const { applyCuratorKnowledgeUpdates: applyCuratorKnowledgeUpdates2 } = await import("./curator-p0dwpn2x.js");
23940
23940
  return applyCuratorKnowledgeUpdates2(directory, recommendations, knowledgeConfig, generation);
23941
23941
  },
23942
23942
  checkHivePromotions: async (entries, knowledgeConfig, directory) => {
23943
- const { checkHivePromotions } = await import("./hive-promoter-dmwjya83.js");
23943
+ const { checkHivePromotions } = await import("./hive-promoter-ppbrv8m1.js");
23944
23944
  return checkHivePromotions(entries, knowledgeConfig, directory);
23945
23945
  },
23946
23946
  applyProposalTriage: async (directory, triage) => {
@@ -23949,7 +23949,7 @@ var _internals27 = {
23949
23949
  activateProposal,
23950
23950
  listSkills: listSkills2,
23951
23951
  sanitizeSlug
23952
- } = await import("./skill-generator-k1e7nee3.js");
23952
+ } = await import("./skill-generator-x6b05jz6.js");
23953
23953
  const result = {
23954
23954
  approved: [],
23955
23955
  rejected: [],
@@ -25279,9 +25279,9 @@ function hashContent(content) {
25279
25279
  return createHash13("sha1").update(content).digest("hex");
25280
25280
  }
25281
25281
  async function loadConfigForPolicyCurator(directory) {
25282
- const { KnowledgeConfigSchema: KnowledgeConfigSchema2 } = await import("./schema-xb7d31wq.js");
25282
+ const { KnowledgeConfigSchema: KnowledgeConfigSchema2 } = await import("./schema-9ebqj7mv.js");
25283
25283
  try {
25284
- const { loadPluginConfigWithMeta: loadPluginConfigWithMeta2 } = await import("./index-9kp3cp5n.js");
25284
+ const { loadPluginConfigWithMeta: loadPluginConfigWithMeta2 } = await import("./index-1wapt173.js");
25285
25285
  const { config: loadedConfig } = loadPluginConfigWithMeta2(directory);
25286
25286
  return KnowledgeConfigSchema2.parse(loadedConfig.knowledge ?? {});
25287
25287
  } catch {
@@ -27434,6 +27434,10 @@ class MemoryValidationError extends Error {
27434
27434
  }
27435
27435
  }
27436
27436
 
27437
+ // src/memory/relation-constants.ts
27438
+ var MAX_MERGE_PARTICIPANTS = 8;
27439
+ var MAX_RELATIONS_PER_MEMORY = 32;
27440
+ var RELATED_RECALL_FANOUT = 2;
27437
27441
  // src/memory/schema.ts
27438
27442
  init_zod();
27439
27443
  import { createHash as createHash14 } from "crypto";
@@ -27583,6 +27587,10 @@ var MemoryRecordSchema = exports_external.object({
27583
27587
  updatedAt: exports_external.string().datetime(),
27584
27588
  lastAccessedAt: exports_external.string().datetime().optional(),
27585
27589
  expiresAt: exports_external.string().datetime().optional(),
27590
+ relations: exports_external.array(exports_external.object({
27591
+ memoryId: exports_external.string().regex(/^mem_[a-f0-9]{16}$/),
27592
+ type: exports_external.literal("merged_with")
27593
+ }).strict()).max(32).optional(),
27586
27594
  qValue: exports_external.number().min(0).max(1).optional(),
27587
27595
  supersedes: exports_external.array(exports_external.string()).optional(),
27588
27596
  supersededBy: exports_external.string().optional(),
@@ -27614,7 +27622,7 @@ var MemoryProposalSchema = exports_external.object({
27614
27622
  ]),
27615
27623
  proposedRecord: MemoryRecordSchema.optional(),
27616
27624
  targetMemoryId: exports_external.string().optional(),
27617
- relatedMemoryIds: exports_external.array(exports_external.string()).optional(),
27625
+ relatedMemoryIds: exports_external.array(exports_external.string().regex(/^mem_[a-f0-9]{16}$/)).max(MAX_MERGE_PARTICIPANTS).optional(),
27618
27626
  proposedBy: exports_external.object({
27619
27627
  agentRole: exports_external.string().optional(),
27620
27628
  agentId: exports_external.string().optional(),
@@ -27634,7 +27642,18 @@ var MemoryProposalSchema = exports_external.object({
27634
27642
  rejectionReason: exports_external.string().optional(),
27635
27643
  createdAt: exports_external.string().datetime(),
27636
27644
  metadata: exports_external.record(exports_external.string(), exports_external.unknown())
27637
- }).strict();
27645
+ }).strict().superRefine((proposal, context) => {
27646
+ if (proposal.operation !== "merge")
27647
+ return;
27648
+ const ids = proposal.relatedMemoryIds ?? [];
27649
+ if (ids.length < 2 || new Set(ids).size !== ids.length) {
27650
+ context.addIssue({
27651
+ code: exports_external.ZodIssueCode.custom,
27652
+ path: ["relatedMemoryIds"],
27653
+ message: `merge proposals require 2-${MAX_MERGE_PARTICIPANTS} distinct relatedMemoryIds`
27654
+ });
27655
+ }
27656
+ });
27638
27657
  var NewMemoryRecordSchema = exports_external.object({
27639
27658
  scope: MemoryScopeRefSchema.optional(),
27640
27659
  kind: MemoryKindSchema,
@@ -27686,6 +27705,14 @@ var CuratorMemoryDecisionSchema = exports_external.discriminatedUnion("action",
27686
27705
  replacement: NewMemoryRecordSchema,
27687
27706
  reason: CuratorDecisionReasonSchema
27688
27707
  }).strict(),
27708
+ exports_external.object({
27709
+ action: exports_external.literal("merge"),
27710
+ proposalId: ProposalIdSchema,
27711
+ relatedMemoryIds: exports_external.array(MemoryIdSchema).min(2).max(MAX_MERGE_PARTICIPANTS).refine((ids) => new Set(ids).size === ids.length, {
27712
+ message: "merge decisions require distinct relatedMemoryIds"
27713
+ }),
27714
+ reason: CuratorDecisionReasonSchema
27715
+ }).strict(),
27689
27716
  exports_external.object({
27690
27717
  action: exports_external.literal("reject"),
27691
27718
  proposalId: ProposalIdSchema,
@@ -27790,102 +27817,6 @@ function validateMemoryProposal(proposal) {
27790
27817
  return MemoryProposalSchema.parse(proposal);
27791
27818
  }
27792
27819
 
27793
- // src/memory/curator-decision-helpers.ts
27794
- var CURATOR_PROMOTED_MEMORY_MAX_TEXT_LENGTH = 500;
27795
- function validateDecisionMatchesProposal(decision, proposal) {
27796
- if (decision.action === "add" && proposal.operation !== "add" || decision.action === "update" && proposal.operation !== "update" || decision.action === "supersede" && proposal.operation !== "supersede") {
27797
- throw new MemoryValidationError(`curator ${decision.action} decision does not match ${proposal.operation} proposal`);
27798
- }
27799
- if (decision.action === "update" && proposal.targetMemoryId && proposal.targetMemoryId !== decision.targetMemoryId) {
27800
- throw new MemoryValidationError("curator update decision target does not match proposal target");
27801
- }
27802
- if (decision.action === "supersede" && proposal.targetMemoryId && proposal.targetMemoryId !== decision.oldMemoryId) {
27803
- throw new MemoryValidationError("curator supersede decision target does not match proposal target");
27804
- }
27805
- }
27806
- function validateCuratorPromotableMemory(record) {
27807
- if (record.stability !== "durable") {
27808
- throw new MemoryValidationError("curator memory promotions must be durable facts");
27809
- }
27810
- if (!DURABLE_MEMORY_KINDS.has(record.kind)) {
27811
- throw new MemoryValidationError("curator memory promotions must use durable fact kinds; store raw docs, search results, and other bulky source material as evidence records instead");
27812
- }
27813
- if (normalizeMemoryText(record.text).length > CURATOR_PROMOTED_MEMORY_MAX_TEXT_LENGTH) {
27814
- throw new MemoryValidationError(`curator memory promotions must be concise durable facts under ${CURATOR_PROMOTED_MEMORY_MAX_TEXT_LENGTH} characters`);
27815
- }
27816
- }
27817
- function applyPatchToMemory(existing, patch, updatedAt) {
27818
- const base = {
27819
- scope: patch.scope ?? existing.scope,
27820
- kind: patch.kind ?? existing.kind,
27821
- text: patch.text === undefined ? existing.text : normalizeMemoryText(patch.text)
27822
- };
27823
- const tags = patch.tags === undefined ? existing.tags : normalizeTags(patch.tags);
27824
- return {
27825
- ...existing,
27826
- ...patch,
27827
- ...base,
27828
- id: createMemoryId(base),
27829
- tags,
27830
- updatedAt,
27831
- contentHash: computeMemoryContentHash(base),
27832
- metadata: patch.metadata === undefined ? existing.metadata : { ...existing.metadata, ...patch.metadata }
27833
- };
27834
- }
27835
- function markProposalReviewed(proposal, decision, status, reviewedAt, ids) {
27836
- const reason = curatorDecisionReason(decision);
27837
- return validateMemoryProposal({
27838
- ...proposal,
27839
- status,
27840
- reviewer: "curator_agent",
27841
- reviewedAt,
27842
- rejectionReason: decision.action === "reject" ? reason : undefined,
27843
- metadata: {
27844
- ...proposal.metadata,
27845
- curatorDecision: {
27846
- action: decision.action,
27847
- reason,
27848
- ...ids,
27849
- appliedAt: reviewedAt
27850
- }
27851
- }
27852
- });
27853
- }
27854
- function curatorDecisionReason(decision) {
27855
- switch (decision.action) {
27856
- case "add":
27857
- return;
27858
- case "update":
27859
- case "supersede":
27860
- case "reject":
27861
- case "noop":
27862
- return decision.reason;
27863
- }
27864
- }
27865
- function buildCuratorDecisionEvent(change, proposal) {
27866
- return {
27867
- ...change,
27868
- proposalOperation: proposal.operation
27869
- };
27870
- }
27871
- function normalizeTags(tags) {
27872
- return Array.from(new Set(tags.map((tag) => tag.toLowerCase().replace(/[^\w-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "")).filter(Boolean))).slice(0, 32);
27873
- }
27874
-
27875
- // src/memory/local-jsonl-provider.ts
27876
- var import_proper_lockfile6 = __toESM(require_proper_lockfile(), 1);
27877
- import { randomUUID as randomUUID6 } from "crypto";
27878
- import { existsSync as existsSync17, readFileSync as readFileSync15, writeFileSync as writeFileSync8 } from "fs";
27879
- import {
27880
- appendFile as appendFile3,
27881
- mkdir as mkdir9,
27882
- readFile as readFile10,
27883
- rename as rename5,
27884
- truncate,
27885
- writeFile as writeFile7
27886
- } from "fs/promises";
27887
- import * as path32 from "path";
27888
-
27889
27820
  // src/memory/q-learning.ts
27890
27821
  function clamp01(value) {
27891
27822
  if (value < 0)
@@ -28115,6 +28046,10 @@ function sameScope(a, b) {
28115
28046
  function scopeAllowed(recordScope, allowedScopes) {
28116
28047
  return allowedScopes.some((scope) => sameScope(recordScope, scope));
28117
28048
  }
28049
+ function scoreMemoryRecord(record, request, qLearningConfig = DEFAULT_QLEARNING_CONFIG) {
28050
+ const result = scoreMemoryRecordDetailed(record, request, createScoringContext(request), qLearningConfig);
28051
+ return result.item;
28052
+ }
28118
28053
  function scoreMemoryRecordDetailed(record, request, context, qLearningConfig = DEFAULT_QLEARNING_CONFIG) {
28119
28054
  if (!request.includeExpired && isExpired(record)) {
28120
28055
  return { item: null, skipReason: "filtered" };
@@ -28280,6 +28215,218 @@ function unionTokens(...sets) {
28280
28215
  return union;
28281
28216
  }
28282
28217
 
28218
+ // src/memory/relations.ts
28219
+ function canonicalMemoryIds(ids) {
28220
+ return Array.from(new Set(ids)).sort();
28221
+ }
28222
+ function buildMemoryRelationIndex(proposals) {
28223
+ const index = new Map;
28224
+ for (const proposal of proposals) {
28225
+ if (proposal.operation !== "merge" || proposal.status !== "applied")
28226
+ continue;
28227
+ const ids = canonicalMemoryIds(proposal.relatedMemoryIds ?? []);
28228
+ for (const sourceId of ids) {
28229
+ let related = index.get(sourceId);
28230
+ if (!related) {
28231
+ related = new Map;
28232
+ index.set(sourceId, related);
28233
+ }
28234
+ for (const targetId of ids) {
28235
+ if (targetId !== sourceId) {
28236
+ related.set(targetId, { memoryId: targetId, type: "merged_with" });
28237
+ }
28238
+ }
28239
+ }
28240
+ }
28241
+ return new Map(Array.from(index, ([id, relations]) => [
28242
+ id,
28243
+ Array.from(relations.values()).sort((a, b) => a.memoryId.localeCompare(b.memoryId))
28244
+ ]));
28245
+ }
28246
+ function projectMemoryRelations(records, proposals) {
28247
+ const index = buildMemoryRelationIndex(proposals);
28248
+ return records.map(({ relations: _derived, ...record }) => {
28249
+ const relations = index.get(record.id);
28250
+ return relations?.length ? { ...record, relations } : record;
28251
+ });
28252
+ }
28253
+ function stripDerivedRelations(record) {
28254
+ const { relations: _derived, ...stored } = record;
28255
+ return stored;
28256
+ }
28257
+ function validateMergeParticipants(ids, records, proposals, now2 = new Date) {
28258
+ const canonical = canonicalMemoryIds(ids);
28259
+ if (canonical.length < 2 || canonical.length > MAX_MERGE_PARTICIPANTS) {
28260
+ throw new MemoryValidationError(`merge decisions require 2-${MAX_MERGE_PARTICIPANTS} distinct relatedMemoryIds`);
28261
+ }
28262
+ const byId = new Map(Array.from(records, (record) => [record.id, record]));
28263
+ const participants = canonical.map((id) => {
28264
+ const record = byId.get(id);
28265
+ if (!record || record.metadata.deleted === true || record.supersededBy || isExpired(record, now2)) {
28266
+ throw new MemoryValidationError(`merge participant is not active: ${id}`);
28267
+ }
28268
+ return record;
28269
+ });
28270
+ const scopeKey = stableScopeKey(participants[0].scope);
28271
+ if (participants.some((record) => stableScopeKey(record.scope) !== scopeKey)) {
28272
+ throw new MemoryValidationError("merge participants must share one memory scope");
28273
+ }
28274
+ const index = buildMemoryRelationIndex(proposals);
28275
+ for (const id of canonical) {
28276
+ const degree = new Set([
28277
+ ...index.get(id)?.map((relation) => relation.memoryId) ?? [],
28278
+ ...canonical.filter((candidate) => candidate !== id)
28279
+ ]).size;
28280
+ if (degree > MAX_RELATIONS_PER_MEMORY) {
28281
+ throw new MemoryValidationError(`merge would exceed ${MAX_RELATIONS_PER_MEMORY} relations for ${id}`);
28282
+ }
28283
+ }
28284
+ return canonical;
28285
+ }
28286
+ function expandRelatedRecallItems(items, allRecords, request) {
28287
+ const normal = items.filter((item) => !item.explored).slice(0, request.maxItems);
28288
+ const explored = items.filter((item) => item.explored);
28289
+ if (normal.length < request.maxItems) {
28290
+ const byId = new Map(allRecords.map((record) => [record.id, record]));
28291
+ const selected = new Set([...normal, ...explored].map((item) => item.record.id));
28292
+ const directSources = [...normal];
28293
+ for (const source of directSources) {
28294
+ const sourceRecord = byId.get(source.record.id) ?? source.record;
28295
+ let addedForSource = 0;
28296
+ for (const relation of sourceRecord.relations ?? []) {
28297
+ if (normal.length >= request.maxItems || addedForSource >= RELATED_RECALL_FANOUT) {
28298
+ break;
28299
+ }
28300
+ if (selected.has(relation.memoryId))
28301
+ continue;
28302
+ const record = byId.get(relation.memoryId);
28303
+ if (!record)
28304
+ continue;
28305
+ const scored = scoreMemoryRecord(record, {
28306
+ ...request,
28307
+ requireQuerySignal: false
28308
+ });
28309
+ if (!scored)
28310
+ continue;
28311
+ normal.push({
28312
+ ...scored,
28313
+ reason: `${scored.reason}, relation=${relation.type}, related_from=${source.record.id}`,
28314
+ relation: { type: relation.type, sourceMemoryId: source.record.id }
28315
+ });
28316
+ selected.add(record.id);
28317
+ addedForSource++;
28318
+ }
28319
+ }
28320
+ }
28321
+ const seen = new Set;
28322
+ return [...normal, ...explored].filter((item) => {
28323
+ if (seen.has(item.record.id))
28324
+ return false;
28325
+ seen.add(item.record.id);
28326
+ return true;
28327
+ });
28328
+ }
28329
+
28330
+ // src/memory/curator-decision-helpers.ts
28331
+ var CURATOR_PROMOTED_MEMORY_MAX_TEXT_LENGTH = 500;
28332
+ function validateDecisionMatchesProposal(decision, proposal) {
28333
+ if (decision.action === "add" && proposal.operation !== "add" || decision.action === "update" && proposal.operation !== "update" || decision.action === "supersede" && proposal.operation !== "supersede" || decision.action === "merge" && proposal.operation !== "merge") {
28334
+ throw new MemoryValidationError(`curator ${decision.action} decision does not match ${proposal.operation} proposal`);
28335
+ }
28336
+ if (decision.action === "update" && proposal.targetMemoryId && proposal.targetMemoryId !== decision.targetMemoryId) {
28337
+ throw new MemoryValidationError("curator update decision target does not match proposal target");
28338
+ }
28339
+ if (decision.action === "supersede" && proposal.targetMemoryId && proposal.targetMemoryId !== decision.oldMemoryId) {
28340
+ throw new MemoryValidationError("curator supersede decision target does not match proposal target");
28341
+ }
28342
+ if (decision.action === "merge" && canonicalMemoryIds(proposal.relatedMemoryIds ?? []).join("\x00") !== canonicalMemoryIds(decision.relatedMemoryIds).join("\x00")) {
28343
+ throw new MemoryValidationError("curator merge decision related memories do not match proposal");
28344
+ }
28345
+ }
28346
+ function validateCuratorPromotableMemory(record) {
28347
+ if (record.stability !== "durable") {
28348
+ throw new MemoryValidationError("curator memory promotions must be durable facts");
28349
+ }
28350
+ if (!DURABLE_MEMORY_KINDS.has(record.kind)) {
28351
+ throw new MemoryValidationError("curator memory promotions must use durable fact kinds; store raw docs, search results, and other bulky source material as evidence records instead");
28352
+ }
28353
+ if (normalizeMemoryText(record.text).length > CURATOR_PROMOTED_MEMORY_MAX_TEXT_LENGTH) {
28354
+ throw new MemoryValidationError(`curator memory promotions must be concise durable facts under ${CURATOR_PROMOTED_MEMORY_MAX_TEXT_LENGTH} characters`);
28355
+ }
28356
+ }
28357
+ function applyPatchToMemory(existing, patch, updatedAt) {
28358
+ const base = {
28359
+ scope: patch.scope ?? existing.scope,
28360
+ kind: patch.kind ?? existing.kind,
28361
+ text: patch.text === undefined ? existing.text : normalizeMemoryText(patch.text)
28362
+ };
28363
+ const tags = patch.tags === undefined ? existing.tags : normalizeTags(patch.tags);
28364
+ return {
28365
+ ...existing,
28366
+ ...patch,
28367
+ ...base,
28368
+ id: createMemoryId(base),
28369
+ tags,
28370
+ updatedAt,
28371
+ contentHash: computeMemoryContentHash(base),
28372
+ metadata: patch.metadata === undefined ? existing.metadata : { ...existing.metadata, ...patch.metadata }
28373
+ };
28374
+ }
28375
+ function markProposalReviewed(proposal, decision, status, reviewedAt, ids) {
28376
+ const reason = curatorDecisionReason(decision);
28377
+ return validateMemoryProposal({
28378
+ ...proposal,
28379
+ status,
28380
+ reviewer: "curator_agent",
28381
+ reviewedAt,
28382
+ rejectionReason: decision.action === "reject" ? reason : undefined,
28383
+ metadata: {
28384
+ ...proposal.metadata,
28385
+ curatorDecision: {
28386
+ action: decision.action,
28387
+ reason,
28388
+ ...ids,
28389
+ appliedAt: reviewedAt
28390
+ }
28391
+ }
28392
+ });
28393
+ }
28394
+ function curatorDecisionReason(decision) {
28395
+ switch (decision.action) {
28396
+ case "add":
28397
+ return;
28398
+ case "update":
28399
+ case "supersede":
28400
+ case "merge":
28401
+ case "reject":
28402
+ case "noop":
28403
+ return decision.reason;
28404
+ }
28405
+ }
28406
+ function buildCuratorDecisionEvent(change, proposal) {
28407
+ return {
28408
+ ...change,
28409
+ proposalOperation: proposal.operation
28410
+ };
28411
+ }
28412
+ function normalizeTags(tags) {
28413
+ return Array.from(new Set(tags.map((tag) => tag.toLowerCase().replace(/[^\w-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "")).filter(Boolean))).slice(0, 32);
28414
+ }
28415
+
28416
+ // src/memory/local-jsonl-provider.ts
28417
+ var import_proper_lockfile6 = __toESM(require_proper_lockfile(), 1);
28418
+ import { randomUUID as randomUUID6 } from "crypto";
28419
+ import { existsSync as existsSync17, readFileSync as readFileSync15, writeFileSync as writeFileSync8 } from "fs";
28420
+ import {
28421
+ appendFile as appendFile3,
28422
+ mkdir as mkdir9,
28423
+ readFile as readFile10,
28424
+ rename as rename5,
28425
+ truncate,
28426
+ writeFile as writeFile7
28427
+ } from "fs/promises";
28428
+ import * as path32 from "path";
28429
+
28283
28430
  // src/memory/maintenance.ts
28284
28431
  var DEFAULT_IMPORTANCE_WEIGHTS = {
28285
28432
  wRecency: 0.2,
@@ -28601,6 +28748,56 @@ function compareText(a, b) {
28601
28748
  return a < b ? -1 : a > b ? 1 : 0;
28602
28749
  }
28603
28750
 
28751
+ // src/memory/proposal-load.ts
28752
+ var INVALID_PROPOSAL_LOAD_SAMPLE_LIMIT = 5;
28753
+ var UNKNOWN_PROPOSAL_ID = "<unknown>";
28754
+ var MAX_PROPOSAL_LOAD_REASON_LENGTH = 200;
28755
+ function parseLoadedProposal(value, rejectDurableSecrets) {
28756
+ const proposal = validateMemoryProposal(value);
28757
+ if (proposal.proposedRecord) {
28758
+ validateMemoryRecordRules(proposal.proposedRecord, {
28759
+ rejectDurableSecrets
28760
+ });
28761
+ }
28762
+ return proposal;
28763
+ }
28764
+ function proposalLoadIssueFromValue(value, error2) {
28765
+ return {
28766
+ proposalId: extractProposalId(value) ?? UNKNOWN_PROPOSAL_ID,
28767
+ reason: normalizeProposalLoadReason(error2)
28768
+ };
28769
+ }
28770
+ function proposalLoadIssueFromId(proposalId, error2) {
28771
+ return {
28772
+ proposalId: proposalId || UNKNOWN_PROPOSAL_ID,
28773
+ reason: normalizeProposalLoadReason(error2)
28774
+ };
28775
+ }
28776
+ function buildProposalLoadDiagnostics(issues) {
28777
+ return {
28778
+ invalidCount: issues.length,
28779
+ samples: issues.slice(0, INVALID_PROPOSAL_LOAD_SAMPLE_LIMIT)
28780
+ };
28781
+ }
28782
+ function asStoredProposalValidationError(proposalId, error2) {
28783
+ if (error2 instanceof MemoryValidationError)
28784
+ return error2;
28785
+ return new MemoryValidationError(`stored memory proposal is malformed: ${proposalId} (${normalizeProposalLoadReason(error2)})`, "stored_memory_proposal_invalid");
28786
+ }
28787
+ function extractProposalId(value) {
28788
+ if (!value || typeof value !== "object")
28789
+ return;
28790
+ const candidate = value.id;
28791
+ return typeof candidate === "string" && candidate.length > 0 ? candidate : undefined;
28792
+ }
28793
+ function normalizeProposalLoadReason(error2) {
28794
+ const raw = error2 instanceof Error ? error2.message : String(error2);
28795
+ const trimmed = raw.replace(/\s+/g, " ").trim();
28796
+ if (trimmed.length <= MAX_PROPOSAL_LOAD_REASON_LENGTH)
28797
+ return trimmed;
28798
+ return `${trimmed.slice(0, MAX_PROPOSAL_LOAD_REASON_LENGTH - 1)}\u2026`;
28799
+ }
28800
+
28604
28801
  // src/memory/local-jsonl-provider.ts
28605
28802
  var lockfile6 = import_proper_lockfile6.default;
28606
28803
 
@@ -28612,6 +28809,7 @@ class LocalJsonlMemoryProvider {
28612
28809
  initialized = false;
28613
28810
  memories = new Map;
28614
28811
  proposals = new Map;
28812
+ lastInvalidProposalLoadSignature = null;
28615
28813
  constructor(rootDirectory, config = {}, vettedCohortRoot) {
28616
28814
  this.rootDirectory = rootDirectory;
28617
28815
  this.cohortRoot = vettedCohortRoot ?? null;
@@ -28634,7 +28832,10 @@ class LocalJsonlMemoryProvider {
28634
28832
  const memoryPath = this.pathFor("memories");
28635
28833
  const proposalPath = this.pathFor("proposals");
28636
28834
  const memoryLoad = validateLoadedMemories(await readJsonl(memoryPath), this.config);
28637
- const proposalLoad = validateLoadedProposals(await readJsonl(proposalPath), this.config);
28835
+ const rawProposalLoad = await readProposalJsonl(proposalPath);
28836
+ const proposalLoad = validateLoadedProposals(rawProposalLoad.values, this.config);
28837
+ proposalLoad.invalidCount += rawProposalLoad.invalidRows.length;
28838
+ proposalLoad.invalidRows.unshift(...rawProposalLoad.invalidRows);
28638
28839
  const outcomeEvents = this.readOutcomeEventsSync();
28639
28840
  const materializedLoad = validateMaterializedMemories(memoryLoad.records, outcomeEvents, this.config);
28640
28841
  this.memories = new Map(materializedLoad.records.map((record) => [record.id, record]));
@@ -28645,7 +28846,9 @@ class LocalJsonlMemoryProvider {
28645
28846
  await this.audit("invalid_load", "memories", `${invalidMemoryCount} invalid memory JSONL row(s) skipped`);
28646
28847
  }
28647
28848
  if (proposalLoad.invalidCount > 0) {
28648
- await this.audit("invalid_load", "proposals", `${proposalLoad.invalidCount} invalid proposal JSONL row(s) skipped`);
28849
+ await this.reportInvalidProposalLoad(proposalLoad.invalidRows);
28850
+ } else {
28851
+ this.lastInvalidProposalLoadSignature = null;
28649
28852
  }
28650
28853
  }
28651
28854
  async upsert(record) {
@@ -28657,7 +28860,7 @@ class LocalJsonlMemoryProvider {
28657
28860
  throw new MemoryValidationError("memory is tombstoned and cannot be upserted");
28658
28861
  }
28659
28862
  let parsed = validateMemoryRecordRules({
28660
- ...record,
28863
+ ...stripDerivedRelations(record),
28661
28864
  createdAt: existing?.createdAt ?? record.createdAt,
28662
28865
  metadata: {
28663
28866
  ...record.metadata,
@@ -28702,7 +28905,9 @@ class LocalJsonlMemoryProvider {
28702
28905
  await this.initialize();
28703
28906
  return this.withOutcomeStoreLock(async () => {
28704
28907
  await this.refreshMemoriesUnlocked();
28705
- return this.memories.get(id) ?? null;
28908
+ await this.refreshProposalsUnlocked();
28909
+ const record = this.memories.get(id);
28910
+ return record ? projectMemoryRelations([record], this.proposals.values())[0] : null;
28706
28911
  });
28707
28912
  }
28708
28913
  async appendOutcome(memoryId, event, anchors = []) {
@@ -28790,12 +28995,13 @@ class LocalJsonlMemoryProvider {
28790
28995
  });
28791
28996
  const result = scoreMemoryRecordsWithDiagnostics(records, request, this.config.qLearning);
28792
28997
  const sliced = sliceRecallItemsWithExploration(result.items, request.maxItems);
28998
+ const expanded = expandRelatedRecallItems(sliced, projectMemoryRelations(Array.from(this.memories.values()), this.proposals.values()), request);
28793
28999
  return {
28794
- items: sliced,
29000
+ items: expanded,
28795
29001
  diagnostics: {
28796
29002
  ...result.diagnostics,
28797
- exploredCount: sliced.some((item) => item.explored) ? 1 : 0,
28798
- returnedCount: sliced.length
29003
+ exploredCount: expanded.some((item) => item.explored) ? 1 : 0,
29004
+ returnedCount: expanded.length
28799
29005
  }
28800
29006
  };
28801
29007
  }
@@ -28848,8 +29054,9 @@ class LocalJsonlMemoryProvider {
28848
29054
  await this.initialize();
28849
29055
  await this.withOutcomeStoreLock(async () => {
28850
29056
  await this.refreshMemoriesUnlocked();
29057
+ await this.refreshProposalsUnlocked();
28851
29058
  });
28852
- let records = Array.from(this.memories.values());
29059
+ let records = projectMemoryRelations(Array.from(this.memories.values()), this.proposals.values());
28853
29060
  if (filter.scopes && filter.scopes.length > 0) {
28854
29061
  records = records.filter((record) => scopeAllowed(record.scope, filter.scopes ?? []));
28855
29062
  }
@@ -28874,8 +29081,13 @@ class LocalJsonlMemoryProvider {
28874
29081
  async createProposal(proposal) {
28875
29082
  await this.initialize();
28876
29083
  const next = validateMemoryProposal(proposal);
28877
- this.proposals.set(next.id, next);
28878
- await appendJsonl(this.pathFor("proposals"), next);
29084
+ await this.withOutcomeStoreLock(async () => {
29085
+ await this.refreshProposalsUnlocked();
29086
+ const filePath = this.pathFor("proposals");
29087
+ await repairIncompleteJsonlTail(filePath);
29088
+ await appendJsonl(filePath, next);
29089
+ this.proposals.set(next.id, next);
29090
+ });
28879
29091
  await this.audit("proposal", next.id);
28880
29092
  this.bumpCohortGeneration();
28881
29093
  return next;
@@ -28968,11 +29180,24 @@ class LocalJsonlMemoryProvider {
28968
29180
  oldMemoryId = oldMemory.id;
28969
29181
  replacementMemoryId = replacement.id;
28970
29182
  memoryId = replacement.id;
29183
+ } else if (decision.action === "merge") {
29184
+ decision = {
29185
+ ...decision,
29186
+ relatedMemoryIds: validateMergeParticipants(decision.relatedMemoryIds, this.memories.values(), this.proposals.values(), new Date(appliedAt))
29187
+ };
28971
29188
  }
28972
29189
  const proposalStatus = decision.action === "reject" ? "rejected" : "applied";
28973
- const reviewedProposal = markProposalReviewed(proposal, decision, proposalStatus, appliedAt, { memoryId, targetMemoryId, oldMemoryId, replacementMemoryId });
29190
+ const reviewedProposal = markProposalReviewed(proposal, decision, proposalStatus, appliedAt, {
29191
+ memoryId,
29192
+ targetMemoryId,
29193
+ oldMemoryId,
29194
+ replacementMemoryId,
29195
+ relatedMemoryIds: decision.action === "merge" ? decision.relatedMemoryIds : undefined
29196
+ });
29197
+ const proposalsPath = this.pathFor("proposals");
29198
+ await repairIncompleteJsonlTail(proposalsPath);
29199
+ await appendJsonl(proposalsPath, reviewedProposal);
28974
29200
  this.proposals.set(reviewedProposal.id, reviewedProposal);
28975
- await appendJsonl(this.pathFor("proposals"), reviewedProposal);
28976
29201
  const change = {
28977
29202
  action: decision.action,
28978
29203
  proposalId: decision.proposalId,
@@ -28982,9 +29207,16 @@ class LocalJsonlMemoryProvider {
28982
29207
  targetMemoryId,
28983
29208
  oldMemoryId,
28984
29209
  replacementMemoryId,
29210
+ relatedMemoryIds: decision.action === "merge" ? decision.relatedMemoryIds : undefined,
28985
29211
  reason: curatorDecisionReason(decision)
28986
29212
  };
28987
- await this.audit("curator_decision", decision.proposalId, change.reason, buildCuratorDecisionEvent(change, proposal));
29213
+ try {
29214
+ await this.audit("curator_decision", decision.proposalId, change.reason, buildCuratorDecisionEvent(change, proposal));
29215
+ } catch (error2) {
29216
+ warn("[memory] failed to persist curator decision audit event", {
29217
+ reason: error2 instanceof Error ? error2.message : String(error2)
29218
+ });
29219
+ }
28988
29220
  return change;
28989
29221
  }
28990
29222
  async compact() {
@@ -29070,8 +29302,32 @@ class LocalJsonlMemoryProvider {
29070
29302
  await appendJsonl(filePath, stripMaterializedOutcomes(record));
29071
29303
  }
29072
29304
  async refreshProposalsUnlocked() {
29073
- const loaded = validateLoadedProposals(await readJsonl(this.pathFor("proposals")), this.config);
29305
+ const proposalPath = this.pathFor("proposals");
29306
+ await repairIncompleteJsonlTail(proposalPath);
29307
+ const rawProposalLoad = await readProposalJsonl(proposalPath);
29308
+ const loaded = validateLoadedProposals(rawProposalLoad.values, this.config);
29309
+ loaded.invalidCount += rawProposalLoad.invalidRows.length;
29310
+ loaded.invalidRows.unshift(...rawProposalLoad.invalidRows);
29074
29311
  this.proposals = new Map(loaded.records.map((proposal) => [proposal.id, proposal]));
29312
+ if (loaded.invalidCount > 0) {
29313
+ await this.reportInvalidProposalLoad(loaded.invalidRows);
29314
+ } else {
29315
+ this.lastInvalidProposalLoadSignature = null;
29316
+ }
29317
+ }
29318
+ async reportInvalidProposalLoad(issues) {
29319
+ const eventJson = buildProposalLoadDiagnostics(issues);
29320
+ const signature = JSON.stringify(eventJson);
29321
+ if (signature === this.lastInvalidProposalLoadSignature)
29322
+ return;
29323
+ this.lastInvalidProposalLoadSignature = signature;
29324
+ try {
29325
+ await this.audit("invalid_load", "proposals", `${issues.length} invalid proposal JSONL row(s) skipped`, eventJson);
29326
+ } catch (error2) {
29327
+ warn("[memory] failed to persist invalid proposal load audit event", {
29328
+ reason: error2 instanceof Error ? error2.message : String(error2)
29329
+ });
29330
+ }
29075
29331
  }
29076
29332
  readOutcomeEventsSync() {
29077
29333
  const filePath = this.pathFor("outcome-events");
@@ -29230,21 +29486,18 @@ function compareText2(a, b) {
29230
29486
  }
29231
29487
  function validateLoadedProposals(values, config) {
29232
29488
  const records = [];
29489
+ const invalidRows = [];
29233
29490
  let invalidCount = 0;
29234
29491
  for (const value of values) {
29235
29492
  try {
29236
- const proposal = validateMemoryProposal(value);
29237
- if (proposal.proposedRecord) {
29238
- validateMemoryRecordRules(proposal.proposedRecord, {
29239
- rejectDurableSecrets: config.redaction.rejectDurableSecrets
29240
- });
29241
- }
29493
+ const proposal = parseLoadedProposal(value, config.redaction.rejectDurableSecrets);
29242
29494
  records.push(proposal);
29243
- } catch {
29495
+ } catch (error2) {
29244
29496
  invalidCount++;
29497
+ invalidRows.push(proposalLoadIssueFromValue(value, error2));
29245
29498
  }
29246
29499
  }
29247
- return { records, invalidCount };
29500
+ return { records, invalidCount, invalidRows };
29248
29501
  }
29249
29502
  async function readJsonl(filePath) {
29250
29503
  if (!existsSync17(filePath))
@@ -29262,6 +29515,28 @@ async function readJsonl(filePath) {
29262
29515
  }
29263
29516
  return records;
29264
29517
  }
29518
+ async function readProposalJsonl(filePath) {
29519
+ if (!existsSync17(filePath))
29520
+ return { values: [], invalidRows: [] };
29521
+ const content = await readFile10(filePath, "utf-8");
29522
+ const values = [];
29523
+ const invalidRows = [];
29524
+ for (const [index, line] of content.split(`
29525
+ `).entries()) {
29526
+ const trimmed = line.trim();
29527
+ if (!trimmed)
29528
+ continue;
29529
+ try {
29530
+ values.push(JSON.parse(trimmed));
29531
+ } catch {
29532
+ invalidRows.push({
29533
+ proposalId: "<unknown>",
29534
+ reason: `invalid JSONL at line ${index + 1}`
29535
+ });
29536
+ }
29537
+ }
29538
+ return { values, invalidRows };
29539
+ }
29265
29540
  async function readAuditEvents(filePath) {
29266
29541
  const values = await readJsonl(filePath);
29267
29542
  const events = [];
@@ -29740,7 +30015,7 @@ async function readLegacyJsonl(rootDirectory, config = {}) {
29740
30015
  const resolved = resolveConfig(config);
29741
30016
  const storageDir = resolveMemoryStorageDir(rootDirectory, resolved);
29742
30017
  const memoryLoad = await readMemoryJsonl(path35.join(storageDir, "memories.jsonl"), resolved);
29743
- const proposalLoad = await readProposalJsonl(path35.join(storageDir, "proposals.jsonl"), resolved);
30018
+ const proposalLoad = await readProposalJsonl2(path35.join(storageDir, "proposals.jsonl"), resolved);
29744
30019
  const outcomeLoad = await readOutcomeJsonl(path35.join(storageDir, "outcome-events.jsonl"));
29745
30020
  return {
29746
30021
  memories: memoryLoad.records,
@@ -29939,24 +30214,20 @@ async function readMemoryJsonl(filePath, config) {
29939
30214
  totalRows: rows.totalRows
29940
30215
  };
29941
30216
  }
29942
- async function readProposalJsonl(filePath, config) {
30217
+ async function readProposalJsonl2(filePath, config) {
29943
30218
  const rows = await readJsonlRows(filePath);
29944
30219
  const records = [];
29945
30220
  const invalidRows = [];
29946
30221
  for (const row of rows.rows) {
29947
30222
  try {
29948
- const proposal = validateMemoryProposal(row.value);
29949
- if (proposal.proposedRecord) {
29950
- validateMemoryRecordRules(proposal.proposedRecord, {
29951
- rejectDurableSecrets: config.redaction.rejectDurableSecrets
29952
- });
29953
- }
30223
+ const proposal = parseLoadedProposal(row.value, config.redaction.rejectDurableSecrets);
29954
30224
  records.push(proposal);
29955
30225
  } catch (err) {
30226
+ const issue = proposalLoadIssueFromValue(row.value, err);
29956
30227
  invalidRows.push({
29957
30228
  file: "proposals.jsonl",
29958
30229
  line: row.line,
29959
- error: err instanceof Error ? err.message : String(err)
30230
+ error: issue.reason
29960
30231
  });
29961
30232
  }
29962
30233
  }
@@ -30299,6 +30570,24 @@ var MIGRATIONS2 = [
30299
30570
  sql: `
30300
30571
  ALTER TABLE memory_events ADD COLUMN prev_hash TEXT;
30301
30572
  `
30573
+ },
30574
+ {
30575
+ version: 14,
30576
+ name: "create_memory_relations",
30577
+ sql: `
30578
+ CREATE TABLE IF NOT EXISTS memory_relations (
30579
+ source_id TEXT NOT NULL,
30580
+ target_id TEXT NOT NULL,
30581
+ proposal_id TEXT NOT NULL,
30582
+ created_at TEXT NOT NULL,
30583
+ PRIMARY KEY (source_id, target_id),
30584
+ FOREIGN KEY (source_id) REFERENCES memory_items(id) ON DELETE CASCADE,
30585
+ FOREIGN KEY (target_id) REFERENCES memory_items(id) ON DELETE CASCADE,
30586
+ FOREIGN KEY (proposal_id) REFERENCES memory_proposals(id)
30587
+ );
30588
+ CREATE INDEX IF NOT EXISTS idx_memory_relations_source
30589
+ ON memory_relations(source_id, target_id);
30590
+ `
30302
30591
  }
30303
30592
  ];
30304
30593
  var EVENT_CHAIN_SCHEMA_MIGRATION_NAME = "add_memory_events_prev_hash";
@@ -30431,6 +30720,14 @@ class SQLiteMemoryProvider {
30431
30720
  await this.migrateLegacyJsonlIfNeeded();
30432
30721
  const memoryLoad = this.loadMemories();
30433
30722
  const proposalLoad = this.loadProposals();
30723
+ for (const proposal of proposalLoad.records) {
30724
+ if (proposal.operation === "merge" && proposal.status === "applied") {
30725
+ this.writeProposal(proposal, {
30726
+ records: memoryLoad.records,
30727
+ proposals: proposalLoad.records
30728
+ });
30729
+ }
30730
+ }
30434
30731
  this.memories = new Map(memoryLoad.records.map((record) => [record.id, record]));
30435
30732
  this.proposals = new Map(proposalLoad.records.map((proposal) => [proposal.id, proposal]));
30436
30733
  this.initialized = true;
@@ -30438,7 +30735,13 @@ class SQLiteMemoryProvider {
30438
30735
  await this.event("invalid_load", "memory_items", `${memoryLoad.invalidCount} invalid SQLite memory row(s) skipped`);
30439
30736
  }
30440
30737
  if (proposalLoad.invalidCount > 0) {
30441
- await this.event("invalid_load", "memory_proposals", `${proposalLoad.invalidCount} invalid SQLite proposal row(s) skipped`);
30738
+ try {
30739
+ this.insertEvent("invalid_load", "memory_proposals", `${proposalLoad.invalidCount} invalid SQLite proposal row(s) skipped`, JSON.stringify(buildProposalLoadDiagnostics(proposalLoad.invalidRows)));
30740
+ } catch (error2) {
30741
+ warn("[memory] failed to persist invalid SQLite proposal load event", {
30742
+ reason: error2 instanceof Error ? error2.message : String(error2)
30743
+ });
30744
+ }
30442
30745
  }
30443
30746
  } catch (err) {
30444
30747
  try {
@@ -30463,7 +30766,8 @@ class SQLiteMemoryProvider {
30463
30766
  if (existing?.metadata.deleted === true) {
30464
30767
  throw new MemoryValidationError("memory is tombstoned and cannot be upserted");
30465
30768
  }
30466
- const merged = existingRow ? mergeProvenanceColumns(record, existingRow) : record;
30769
+ const storedRecord = stripDerivedRelations(record);
30770
+ const merged = existingRow ? mergeProvenanceColumns(storedRecord, existingRow) : storedRecord;
30467
30771
  next = validateMemoryRecordRules({
30468
30772
  ...merged,
30469
30773
  createdAt: existing?.createdAt ?? merged.createdAt,
@@ -30512,7 +30816,7 @@ class SQLiteMemoryProvider {
30512
30816
  const record = this.readMemoryById(id);
30513
30817
  if (record)
30514
30818
  this.memories.set(id, record);
30515
- return record;
30819
+ return record ? this.projectRelationsFromStorage([record])[0] : null;
30516
30820
  }
30517
30821
  async appendOutcome(memoryId, event, anchors = []) {
30518
30822
  await this.initialize();
@@ -30629,12 +30933,13 @@ class SQLiteMemoryProvider {
30629
30933
  const result = scoreMemoryRecordsWithDiagnostics(candidates.records, request, this.config.qLearning);
30630
30934
  const reranked = candidates.ftsOrder ? rerankWithFts(result.items, candidates.ftsOrder) : result.items;
30631
30935
  const disabledPathSliced = sliceRecallItemsWithExploration(reranked, request.maxItems);
30936
+ const expanded2 = this.expandRelated(disabledPathSliced, request);
30632
30937
  return {
30633
- items: disabledPathSliced,
30938
+ items: expanded2,
30634
30939
  diagnostics: {
30635
30940
  ...result.diagnostics,
30636
- exploredCount: disabledPathSliced.some((item) => item.explored) ? 1 : 0,
30637
- returnedCount: disabledPathSliced.length
30941
+ exploredCount: expanded2.some((item) => item.explored) ? 1 : 0,
30942
+ returnedCount: expanded2.length
30638
30943
  }
30639
30944
  };
30640
30945
  }
@@ -30675,12 +30980,13 @@ class SQLiteMemoryProvider {
30675
30980
  });
30676
30981
  }
30677
30982
  const denseFailedSliced = sliceRecallItemsWithExploration(lexicalReranked, request.maxItems);
30983
+ const expanded2 = this.expandRelated(denseFailedSliced, request);
30678
30984
  return {
30679
- items: denseFailedSliced,
30985
+ items: expanded2,
30680
30986
  diagnostics: {
30681
30987
  ...lexicalResult.diagnostics,
30682
- exploredCount: denseFailedSliced.some((item) => item.explored) ? 1 : 0,
30683
- returnedCount: denseFailedSliced.length
30988
+ exploredCount: expanded2.some((item) => item.explored) ? 1 : 0,
30989
+ returnedCount: expanded2.length
30684
30990
  }
30685
30991
  };
30686
30992
  }
@@ -30751,16 +31057,33 @@ class SQLiteMemoryProvider {
30751
31057
  }
30752
31058
  }
30753
31059
  const fusionSliced = sliceRecallItemsWithExploration(rerankedItems, request.maxItems);
31060
+ const expanded = this.expandRelated(fusionSliced, request);
30754
31061
  return {
30755
- items: fusionSliced,
31062
+ items: expanded,
30756
31063
  diagnostics: {
30757
31064
  ...lexicalResult.diagnostics,
30758
- exploredCount: fusionSliced.some((item) => item.explored) ? 1 : 0,
30759
- returnedCount: fusionSliced.length,
31065
+ exploredCount: expanded.some((item) => item.explored) ? 1 : 0,
31066
+ returnedCount: expanded.length,
30760
31067
  fusionActive: true
30761
31068
  }
30762
31069
  };
30763
31070
  }
31071
+ expandRelated(items, request) {
31072
+ const sources = this.projectRelationsFromStorage(items.map((item) => item.record));
31073
+ const sourceById = new Map(sources.map((record) => [record.id, record]));
31074
+ const relatedRecords = [];
31075
+ for (const source of sources) {
31076
+ for (const relation of (source.relations ?? []).slice(0, RELATED_RECALL_FANOUT)) {
31077
+ const record = this.readMemoryById(relation.memoryId);
31078
+ if (record)
31079
+ relatedRecords.push(record);
31080
+ }
31081
+ }
31082
+ return expandRelatedRecallItems(items.map((item) => ({
31083
+ ...item,
31084
+ record: sourceById.get(item.record.id) ?? item.record
31085
+ })), [...sources, ...relatedRecords], request);
31086
+ }
30764
31087
  async recordRecallUsage(event) {
30765
31088
  await this.initialize();
30766
31089
  this.requireDb().run(`INSERT INTO memory_recall_usage (
@@ -30956,7 +31279,7 @@ class SQLiteMemoryProvider {
30956
31279
  return !Number.isFinite(expires) || expires > now2;
30957
31280
  });
30958
31281
  }
30959
- return records;
31282
+ return this.projectRelationsFromStorage(records);
30960
31283
  }
30961
31284
  async createProposal(proposal) {
30962
31285
  await this.initialize();
@@ -30973,7 +31296,7 @@ class SQLiteMemoryProvider {
30973
31296
  }
30974
31297
  async listProposals(filter = {}) {
30975
31298
  await this.initialize();
30976
- let proposals = Array.from(this.proposals.values());
31299
+ let proposals = this.refreshProposalsFromStorage();
30977
31300
  if (filter.status) {
30978
31301
  proposals = proposals.filter((proposal) => proposal.status === filter.status);
30979
31302
  }
@@ -31113,7 +31436,8 @@ class SQLiteMemoryProvider {
31113
31436
  const provenanceById = new Map(this.requireDb().query("SELECT id, source_task_id, agent_role, embedding_model_version, valid_from, supersedes_reason FROM memory_items").all().map((row) => [row.id, row]));
31114
31437
  const enriched = memories.map((memory) => {
31115
31438
  const row = provenanceById.get(memory.id);
31116
- return row ? mergeProvenanceColumns(memory, row) : memory;
31439
+ const stored = stripDerivedRelations(memory);
31440
+ return row ? mergeProvenanceColumns(stored, row) : stored;
31117
31441
  });
31118
31442
  const proposals = await this.listProposals();
31119
31443
  const outcomeEvents = this.readOutcomeEvents();
@@ -31542,21 +31866,41 @@ class SQLiteMemoryProvider {
31542
31866
  loadProposals() {
31543
31867
  const rows = this.requireDb().query("SELECT id, proposal_json FROM memory_proposals ORDER BY created_at ASC").all();
31544
31868
  const records = [];
31869
+ const invalidRows = [];
31545
31870
  let invalidCount = 0;
31546
31871
  for (const row of rows) {
31547
31872
  try {
31548
- const proposal = validateMemoryProposal(JSON.parse(row.proposal_json));
31549
- if (proposal.proposedRecord) {
31550
- validateMemoryRecordRules(proposal.proposedRecord, {
31551
- rejectDurableSecrets: this.config.redaction.rejectDurableSecrets
31552
- });
31553
- }
31873
+ const proposal = parseLoadedProposal(JSON.parse(row.proposal_json), this.config.redaction.rejectDurableSecrets);
31554
31874
  records.push(proposal);
31555
- } catch {
31875
+ } catch (error2) {
31556
31876
  invalidCount++;
31877
+ invalidRows.push(proposalLoadIssueFromId(row.id, error2));
31557
31878
  }
31558
31879
  }
31559
- return { records, invalidCount };
31880
+ return { records, invalidCount, invalidRows };
31881
+ }
31882
+ refreshProposalsFromStorage() {
31883
+ const records = this.loadProposals().records;
31884
+ this.proposals.clear();
31885
+ for (const proposal of records)
31886
+ this.proposals.set(proposal.id, proposal);
31887
+ return records;
31888
+ }
31889
+ projectRelationsFromStorage(records) {
31890
+ if (records.length === 0)
31891
+ return [];
31892
+ const query = this.requireDb().prepare(" SELECT target_id FROM memory_relations WHERE source_id = ? ORDER BY target_id ASC LIMIT ?");
31893
+ try {
31894
+ return records.map(({ relations: _derived, ...record }) => {
31895
+ const relations = query.all(record.id, MAX_RELATIONS_PER_MEMORY).map((row) => ({
31896
+ memoryId: row.target_id,
31897
+ type: "merged_with"
31898
+ }));
31899
+ return relations.length ? { ...record, relations } : record;
31900
+ });
31901
+ } finally {
31902
+ query.finalize();
31903
+ }
31560
31904
  }
31561
31905
  writeMemory(record) {
31562
31906
  const stored = stripMaterializedOutcomes(record);
@@ -31778,7 +32122,7 @@ class SQLiteMemoryProvider {
31778
32122
  this.requireDb().run("DELETE FROM memory_items_vec WHERE id = ?", [id]);
31779
32123
  } catch {}
31780
32124
  }
31781
- writeProposal(proposal) {
32125
+ writeProposal(proposal, validationContext) {
31782
32126
  this.requireDb().run(`INSERT OR REPLACE INTO memory_proposals (
31783
32127
  id,
31784
32128
  status,
@@ -31790,6 +32134,33 @@ class SQLiteMemoryProvider {
31790
32134
  proposal.createdAt,
31791
32135
  JSON.stringify(proposal)
31792
32136
  ]);
32137
+ if (proposal.operation === "merge" && proposal.status === "applied") {
32138
+ this.requireDb().run("DELETE FROM memory_relations WHERE proposal_id = ?", [proposal.id]);
32139
+ let ids;
32140
+ try {
32141
+ ids = validateMergeParticipants(proposal.relatedMemoryIds ?? [], validationContext?.records ?? this.loadMemories().records, validationContext?.proposals ?? this.loadProposals().records, proposal.reviewedAt ? new Date(proposal.reviewedAt) : new Date);
32142
+ } catch (error2) {
32143
+ warn("Skipping invalid applied merge during relation materialization", {
32144
+ proposalId: proposal.id,
32145
+ reason: error2 instanceof Error ? error2.message : String(error2)
32146
+ });
32147
+ return;
32148
+ }
32149
+ const existingIds = new Set(ids.filter((id) => Boolean(this.requireDb().query("SELECT id FROM memory_items WHERE id = ? LIMIT 1").get(id))));
32150
+ for (const sourceId of ids) {
32151
+ for (const targetId of ids) {
32152
+ if (sourceId === targetId || !existingIds.has(sourceId) || !existingIds.has(targetId)) {
32153
+ continue;
32154
+ }
32155
+ this.requireDb().run("INSERT OR IGNORE INTO memory_relations (source_id, target_id, proposal_id, created_at) VALUES (?, ?, ?, ?)", [
32156
+ sourceId,
32157
+ targetId,
32158
+ proposal.id,
32159
+ proposal.reviewedAt ?? proposal.createdAt
32160
+ ]);
32161
+ }
32162
+ }
32163
+ }
31793
32164
  }
31794
32165
  applyDecisionToStorage(decision, proposal, appliedAt) {
31795
32166
  const memories = [];
@@ -31854,13 +32225,21 @@ class SQLiteMemoryProvider {
31854
32225
  oldMemoryId = oldMemory.id;
31855
32226
  replacementMemoryId = replacement.id;
31856
32227
  memoryId = replacement.id;
32228
+ } else if (decision.action === "merge") {
32229
+ const currentMemories = this.loadMemories().records;
32230
+ const currentProposals = this.loadProposals().records;
32231
+ decision = {
32232
+ ...decision,
32233
+ relatedMemoryIds: validateMergeParticipants(decision.relatedMemoryIds, currentMemories, currentProposals, new Date(appliedAt))
32234
+ };
31857
32235
  }
31858
32236
  const proposalStatus = decision.action === "reject" ? "rejected" : "applied";
31859
32237
  const reviewedProposal = markProposalReviewed(proposal, decision, proposalStatus, appliedAt, {
31860
32238
  memoryId,
31861
32239
  targetMemoryId,
31862
32240
  oldMemoryId,
31863
- replacementMemoryId
32241
+ replacementMemoryId,
32242
+ relatedMemoryIds: decision.action === "merge" ? decision.relatedMemoryIds : undefined
31864
32243
  });
31865
32244
  const change = {
31866
32245
  action: decision.action,
@@ -31871,6 +32250,7 @@ class SQLiteMemoryProvider {
31871
32250
  targetMemoryId,
31872
32251
  oldMemoryId,
31873
32252
  replacementMemoryId,
32253
+ relatedMemoryIds: decision.action === "merge" ? decision.relatedMemoryIds : undefined,
31874
32254
  reason: curatorDecisionReason(decision)
31875
32255
  };
31876
32256
  return {
@@ -31885,15 +32265,15 @@ class SQLiteMemoryProvider {
31885
32265
  if (!row) {
31886
32266
  throw new MemoryValidationError("memory proposal was not found");
31887
32267
  }
31888
- const proposal = validateMemoryProposal(JSON.parse(row.proposal_json));
32268
+ let proposal;
32269
+ try {
32270
+ proposal = parseLoadedProposal(JSON.parse(row.proposal_json), this.config.redaction.rejectDurableSecrets);
32271
+ } catch (error2) {
32272
+ throw asStoredProposalValidationError(row.id, error2);
32273
+ }
31889
32274
  if (proposal.status !== "pending") {
31890
32275
  throw new MemoryValidationError("memory proposal is not pending");
31891
32276
  }
31892
- if (proposal.proposedRecord) {
31893
- validateMemoryRecordRules(proposal.proposedRecord, {
31894
- rejectDurableSecrets: this.config.redaction.rejectDurableSecrets
31895
- });
31896
- }
31897
32277
  return proposal;
31898
32278
  }
31899
32279
  readActiveMemory(memoryId) {
@@ -31954,7 +32334,7 @@ class SQLiteMemoryProvider {
31954
32334
  for (const sourceRow of payload.memoryRows) {
31955
32335
  const rawRecord = sourceRow.record;
31956
32336
  const record = (rawRecord.outcomes?.length ?? 0) > 0 ? ensureOutcomeGeneration(rawRecord) : rawRecord;
31957
- this.writeMemory(stripMaterializedOutcomes(record));
32337
+ this.writeMemory(stripDerivedRelations(stripMaterializedOutcomes(record)));
31958
32338
  if (typeof record.metadata.outcomeGeneration === "string") {
31959
32339
  try {
31960
32340
  materializedRows.push({
@@ -37586,7 +37966,7 @@ async function runFinalizeStage(ctx) {
37586
37966
  }
37587
37967
  }
37588
37968
  try {
37589
- const { CuratorConfigSchema: CCS } = await import("./schema-xb7d31wq.js");
37969
+ const { CuratorConfigSchema: CCS } = await import("./schema-9ebqj7mv.js");
37590
37970
  const { config: pmLoadedConfig } = _internals41.loadPluginConfigWithMeta(ctx.directory);
37591
37971
  const curatorCfg = CCS.parse(pmLoadedConfig.curator ?? {});
37592
37972
  if (curatorCfg.enabled && curatorCfg.postmortem_enabled) {
@@ -39810,9 +40190,9 @@ var _internals45 = {
39810
40190
  loadPluginConfigWithMeta,
39811
40191
  loadCuratorDeps: async () => {
39812
40192
  const [{ CuratorConfigSchema }, curator, { createCuratorLLMDelegate: createCuratorLLMDelegate2 }] = await Promise.all([
39813
- import("./schema-xb7d31wq.js"),
39814
- import("./curator-q017rjfk.js"),
39815
- import("./curator-llm-factory-m1ke17eq.js")
40193
+ import("./schema-9ebqj7mv.js"),
40194
+ import("./curator-p0dwpn2x.js"),
40195
+ import("./curator-llm-factory-g65xmh8f.js")
39816
40196
  ]);
39817
40197
  return { CuratorConfigSchema, curator, createCuratorLLMDelegate: createCuratorLLMDelegate2 };
39818
40198
  }
@@ -40821,7 +41201,7 @@ async function computeCurationDiagnostics(directory, entries) {
40821
41201
  let scanCompleted = false;
40822
41202
  let scanRemaining = 0;
40823
41203
  try {
40824
- const { getScanStatus } = await import("./scan-cursor-fbtgyeft.js");
41204
+ const { getScanStatus } = await import("./scan-cursor-wjad4yd7.js");
40825
41205
  const status = await getScanStatus(directory);
40826
41206
  scanGeneration = status.generation;
40827
41207
  scanCompleted = status.completed;
@@ -40829,19 +41209,19 @@ async function computeCurationDiagnostics(directory, entries) {
40829
41209
  } catch {}
40830
41210
  let configMatch = null;
40831
41211
  try {
40832
- const { isLinked: isLinked2 } = await import("./knowledge-link-8knefv8x.js");
41212
+ const { isLinked: isLinked2 } = await import("./knowledge-link-0byjmj0w.js");
40833
41213
  if (isLinked2(directory)) {
40834
- const { KnowledgeConfigSchema: KnowledgeConfigSchema2 } = await import("./schema-xb7d31wq.js");
41214
+ const { KnowledgeConfigSchema: KnowledgeConfigSchema2 } = await import("./schema-9ebqj7mv.js");
40835
41215
  let config;
40836
41216
  try {
40837
- const { loadPluginConfigWithMeta: loadPluginConfigWithMeta2 } = await import("./index-9kp3cp5n.js");
41217
+ const { loadPluginConfigWithMeta: loadPluginConfigWithMeta2 } = await import("./index-1wapt173.js");
40838
41218
  const loaded = loadPluginConfigWithMeta2(directory);
40839
41219
  config = KnowledgeConfigSchema2.parse(loaded.config.knowledge ?? {});
40840
41220
  } catch {
40841
41221
  config = KnowledgeConfigSchema2.parse({});
40842
41222
  }
40843
41223
  const { cohortConfigFingerprint: cohortConfigFingerprint2 } = await import("./config-fingerprint-v22xh5a2.js");
40844
- const { buildConfigFingerprintInput: buildConfigFingerprintInput2, readCohortConfigFingerprint } = await import("./curation-policy-ej9xc0cd.js");
41224
+ const { buildConfigFingerprintInput: buildConfigFingerprintInput2, readCohortConfigFingerprint } = await import("./curation-policy-ayq5kmnf.js");
40845
41225
  const currentFp = cohortConfigFingerprint2(buildConfigFingerprintInput2(config));
40846
41226
  const cohortFp = await readCohortConfigFingerprint(directory);
40847
41227
  configMatch = cohortFp === null ? null : currentFp === cohortFp;
@@ -42182,7 +42562,7 @@ async function handleDoctorCommand(directory, args) {
42182
42562
  const result = runConfigDoctor(config, directory);
42183
42563
  let output;
42184
42564
  if (enableAutoFix && result.hasAutoFixableIssues) {
42185
- const { runConfigDoctorWithFixes } = await import("./config-doctor-1yn317qf.js");
42565
+ const { runConfigDoctorWithFixes } = await import("./config-doctor-636h188h.js");
42186
42566
  const fixResult = await runConfigDoctorWithFixes(directory, config, true, {
42187
42567
  applyLossy: true
42188
42568
  });
@@ -43122,7 +43502,7 @@ async function handleEvidenceCommand(directory, args) {
43122
43502
  return formatTaskEvidenceMarkdown(evidenceData);
43123
43503
  }
43124
43504
  async function handleEvidenceSummaryCommand(directory) {
43125
- const { buildEvidenceSummary } = await import("./evidence-summary-service-6jzfa547.js");
43505
+ const { buildEvidenceSummary } = await import("./evidence-summary-service-hvbqyb4g.js");
43126
43506
  const artifact = await buildEvidenceSummary(directory);
43127
43507
  if (!artifact) {
43128
43508
  return "No plan found. Run `/swarm plan` to check plan status.";
@@ -63033,7 +63413,7 @@ async function mergeStagedSqlite(destStoragePath, stagedPath) {
63033
63413
  return { merged: await countRowsSafe(destDbPath), skipped: 0 };
63034
63414
  }
63035
63415
  try {
63036
- const { loadDatabaseCtor: loadDatabaseCtor2 } = await import("./sqlite-loader-r1kgt2pq.js");
63416
+ const { loadDatabaseCtor: loadDatabaseCtor2 } = await import("./sqlite-loader-c92413n7.js");
63037
63417
  const Db = loadDatabaseCtor2();
63038
63418
  const db = new Db(destDbPath);
63039
63419
  let attached = false;
@@ -63130,7 +63510,7 @@ async function mergeStagedSqlite(destStoragePath, stagedPath) {
63130
63510
  }
63131
63511
  async function countRowsSafe(dbPath) {
63132
63512
  try {
63133
- const { loadDatabaseCtor: loadDatabaseCtor2 } = await import("./sqlite-loader-r1kgt2pq.js");
63513
+ const { loadDatabaseCtor: loadDatabaseCtor2 } = await import("./sqlite-loader-c92413n7.js");
63134
63514
  const Db = loadDatabaseCtor2();
63135
63515
  const db = new Db(dbPath);
63136
63516
  try {
@@ -78481,7 +78861,7 @@ async function runValidationWithTransientRetry(input, candidateContent, baseline
78481
78861
  if (!input.dispatcher) {
78482
78862
  throw new Error("no evaluation dispatcher available (cannot run validation)");
78483
78863
  }
78484
- const { createModelEvaluationExecutor: createModelEvaluationExecutor2 } = await import("./runner-fzgkd6m1.js");
78864
+ const { createModelEvaluationExecutor: createModelEvaluationExecutor2 } = await import("./runner-rddddxmq.js");
78485
78865
  const executor = createModelEvaluationExecutor2(input.dispatcher, input.sessionId);
78486
78866
  const result = await withProtectedSetIntegrity([evalInputRoot, ...protectedRoots(input.directory)], () => _internals98.evaluateCandidateV1({
78487
78867
  projectRoot: input.directory,
@@ -80606,7 +80986,7 @@ function buildDetailedHelp(commandName, entry) {
80606
80986
  async function handleHelpCommand(ctx) {
80607
80987
  const targetCommand = ctx.args.join(" ");
80608
80988
  if (!targetCommand) {
80609
- const { buildHelpText } = await import("./index-e0re84cx.js");
80989
+ const { buildHelpText } = await import("./index-25xvw7hq.js");
80610
80990
  return buildHelpText();
80611
80991
  }
80612
80992
  const tokens = targetCommand.split(/\s+/);
@@ -80615,7 +80995,7 @@ async function handleHelpCommand(ctx) {
80615
80995
  return _internals101.buildDetailedHelp(resolved.key, resolved.entry);
80616
80996
  }
80617
80997
  const similar = _internals101.findSimilarCommands(targetCommand);
80618
- const { buildHelpText: fullHelp } = await import("./index-e0re84cx.js");
80998
+ const { buildHelpText: fullHelp } = await import("./index-25xvw7hq.js");
80619
80999
  if (similar.length > 0) {
80620
81000
  return `Command '/swarm ${targetCommand}' not found.
80621
81001
 
@@ -80946,7 +81326,7 @@ var COMMAND_REGISTRY = {
80946
81326
  },
80947
81327
  "guardrail explain": {
80948
81328
  handler: async (ctx) => {
80949
- const { handleGuardrailExplain } = await import("./guardrail-explain-xw3ma90f.js");
81329
+ const { handleGuardrailExplain } = await import("./guardrail-explain-s0dzn8c6.js");
80950
81330
  return handleGuardrailExplain(ctx.directory, ctx.args);
80951
81331
  },
80952
81332
  description: "Dry-run: show what the guardrails would do to a command or write target (executes nothing)",
@@ -80956,7 +81336,7 @@ var COMMAND_REGISTRY = {
80956
81336
  },
80957
81337
  "guardrail reset": {
80958
81338
  handler: async (ctx) => {
80959
- const { handleGuardrailReset } = await import("./guardrail-reset-z9fkxksy.js");
81339
+ const { handleGuardrailReset } = await import("./guardrail-reset-cxkf5h0x.js");
80960
81340
  return handleGuardrailReset(ctx.args, ctx.sessionID);
80961
81341
  },
80962
81342
  description: "Reset one exact active invocation/action circuit after repair",
@@ -80965,7 +81345,7 @@ var COMMAND_REGISTRY = {
80965
81345
  },
80966
81346
  "guardrail-explain": {
80967
81347
  handler: async (ctx) => {
80968
- const { handleGuardrailExplain } = await import("./guardrail-explain-xw3ma90f.js");
81348
+ const { handleGuardrailExplain } = await import("./guardrail-explain-s0dzn8c6.js");
80969
81349
  return handleGuardrailExplain(ctx.directory, ctx.args);
80970
81350
  },
80971
81351
  description: "Dry-run: show what the guardrails would do to a command or write target (executes nothing)",
@@ -80975,7 +81355,7 @@ var COMMAND_REGISTRY = {
80975
81355
  },
80976
81356
  "guardrail-reset": {
80977
81357
  handler: async (ctx) => {
80978
- const { handleGuardrailReset } = await import("./guardrail-reset-z9fkxksy.js");
81358
+ const { handleGuardrailReset } = await import("./guardrail-reset-cxkf5h0x.js");
80979
81359
  return handleGuardrailReset(ctx.args, ctx.sessionID);
80980
81360
  },
80981
81361
  description: "Reset one exact active invocation/action circuit after repair",
@@ -80985,7 +81365,7 @@ var COMMAND_REGISTRY = {
80985
81365
  },
80986
81366
  "guardrail-log": {
80987
81367
  handler: async (ctx) => {
80988
- const { handleGuardrailLog } = await import("./guardrail-log-etmzhbge.js");
81368
+ const { handleGuardrailLog } = await import("./guardrail-log-qc7rfhv2.js");
80989
81369
  return handleGuardrailLog(ctx.directory, ctx.args);
80990
81370
  },
80991
81371
  description: "Read the guardrail decision log (use --blocks-only for blocks)",
@@ -86981,7 +87361,7 @@ function getDisplayFinalPromptPressure() {
86981
87361
  async function rehydratePrSubscriptions(sessionID, directory) {
86982
87362
  const map = new Map;
86983
87363
  try {
86984
- const { listActive: listActive2 } = await import("./pr-subscriptions-3zwthnan.js");
87364
+ const { listActive: listActive2 } = await import("./pr-subscriptions-4e64sbwg.js");
86985
87365
  const records = await listActive2(directory);
86986
87366
  for (const record of records) {
86987
87367
  if (record.sessionID !== sessionID)
@@ -87427,7 +87807,7 @@ function isLaneOwnedByCurrentSession(branchName, expectedSessionId) {
87427
87807
  return extracted === expectedSessionId;
87428
87808
  }
87429
87809
  async function inspectStandardWorktreeCollisionOwnership(identity) {
87430
- const collisionOwnership = await import("./worktree-collision-ownership-jfam09nj.js");
87810
+ const collisionOwnership = await import("./worktree-collision-ownership-g5n5xbqy.js");
87431
87811
  return collisionOwnership.inspectStandardWorktreeCollisionOwnership(identity);
87432
87812
  }
87433
87813
  async function runRecoveryGit(directory, args, timeoutMs = 15000) {
@@ -87836,7 +88216,7 @@ async function precreateStandardWorktreeSession(args) {
87836
88216
  const laneProfile = computeLaneRuntimeProfile(worktreeConfig.runtime_isolation, laneIndex, handle.worktreePath);
87837
88217
  if (laneProfile) {
87838
88218
  try {
87839
- const { writeLaneProfileToDiskReal } = await import("./core-k687jrr3.js");
88219
+ const { writeLaneProfileToDiskReal } = await import("./core-h3hsczyq.js");
87840
88220
  await writeLaneProfileToDiskReal(handle.worktreePath, laneProfile.laneIndex, laneProfile.envOverrides);
87841
88221
  } catch {}
87842
88222
  }
@@ -88761,7 +89141,7 @@ async function finishStandardWorktreeDispatch(directory, dispatch, config, callI
88761
89141
  }
88762
89142
  clearWorktreeMergeStatus(statusKey);
88763
89143
  try {
88764
- const { removeLaneProfileFromDiskReal } = await import("./core-k687jrr3.js");
89144
+ const { removeLaneProfileFromDiskReal } = await import("./core-h3hsczyq.js");
88765
89145
  await removeLaneProfileFromDiskReal(dispatch.handle.worktreePath, dispatch.laneIndex);
88766
89146
  } catch {}
88767
89147
  const state = serializationStateBySessionID.get(dispatch.parentSessionID);