claude-mpm 4.0.31__py3-none-any.whl → 4.0.32__py3-none-any.whl

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.
@@ -51,18 +51,10 @@ class AgentMemoryManager(MemoryServiceInterface):
51
51
  # Updated to support 20k tokens (~80KB) for enhanced memory capacity
52
52
  DEFAULT_MEMORY_LIMITS = {
53
53
  "max_file_size_kb": 80, # Increased from 8KB to 80KB (20k tokens)
54
- "max_sections": 10,
55
- "max_items_per_section": 15,
54
+ "max_items": 100, # Maximum total memory items
56
55
  "max_line_length": 120,
57
56
  }
58
57
 
59
- REQUIRED_SECTIONS = [
60
- "Project Architecture",
61
- "Implementation Guidelines",
62
- "Common Mistakes to Avoid",
63
- "Current Technical Context",
64
- ]
65
-
66
58
  def __init__(
67
59
  self, config: Optional[Config] = None, working_directory: Optional[Path] = None
68
60
  ):
@@ -138,12 +130,8 @@ class AgentMemoryManager(MemoryServiceInterface):
138
130
  "max_file_size_kb": config_limits.get(
139
131
  "default_size_kb", self.DEFAULT_MEMORY_LIMITS["max_file_size_kb"]
140
132
  ),
141
- "max_sections": config_limits.get(
142
- "max_sections", self.DEFAULT_MEMORY_LIMITS["max_sections"]
143
- ),
144
- "max_items_per_section": config_limits.get(
145
- "max_items_per_section",
146
- self.DEFAULT_MEMORY_LIMITS["max_items_per_section"],
133
+ "max_items": config_limits.get(
134
+ "max_items", self.DEFAULT_MEMORY_LIMITS["max_items"]
147
135
  ),
148
136
  "max_line_length": config_limits.get(
149
137
  "max_line_length", self.DEFAULT_MEMORY_LIMITS["max_line_length"]
@@ -272,75 +260,41 @@ class AgentMemoryManager(MemoryServiceInterface):
272
260
  self.logger.info(f"Creating default memory for agent: {agent_id}")
273
261
  return self._create_default_memory(agent_id)
274
262
 
275
- def update_agent_memory(self, agent_id: str, section: str, new_item: str) -> bool:
276
- """Add new learning item to specified section.
263
+ def update_agent_memory(self, agent_id: str, new_items: List[str]) -> bool:
264
+ """Add new learning items to agent memory as a simple list.
277
265
 
278
- WHY: Agents discover new patterns and insights during task execution that
279
- should be preserved for future tasks. This method adds new learnings while
280
- enforcing size limits to prevent unbounded growth.
266
+ WHY: Simplified memory system - all memories are stored as a simple list
267
+ without categorization, making it easier to manage and understand.
281
268
 
282
269
  Args:
283
270
  agent_id: The agent identifier
284
- section: The section name to add the item to
285
- new_item: The learning item to add
271
+ new_items: List of new learning items to add
286
272
 
287
273
  Returns:
288
274
  bool: True if update succeeded, False otherwise
289
275
  """
290
276
  try:
291
- current_memory = self.load_agent_memory(agent_id)
292
- updated_memory = self.content_manager.add_item_to_section(
293
- current_memory, section, new_item
294
- )
295
-
296
- # Enforce limits
297
- agent_limits = self._get_agent_limits(agent_id)
298
- if self.content_manager.exceeds_limits(updated_memory, agent_limits):
299
- self.logger.debug(f"Memory for {agent_id} exceeds limits, truncating")
300
- updated_memory = self.content_manager.truncate_to_limits(
301
- updated_memory, agent_limits
302
- )
303
-
304
- # Save with timestamp
305
- return self._save_memory_file(agent_id, updated_memory)
277
+ # Use the simplified _add_learnings_to_memory method
278
+ return self._add_learnings_to_memory(agent_id, new_items)
306
279
  except Exception as e:
307
280
  self.logger.error(f"Error updating memory for {agent_id}: {e}")
308
281
  # Never fail on memory errors
309
282
  return False
310
283
 
311
- def add_learning(self, agent_id: str, learning_type: str, content: str) -> bool:
312
- """Add structured learning to appropriate section.
284
+ def add_learning(self, agent_id: str, content: str) -> bool:
285
+ """Add a learning to agent memory as a simple list item.
313
286
 
314
- WHY: Different types of learnings belong in different sections for better
315
- organization and retrieval. This method maps learning types to appropriate
316
- sections automatically.
287
+ WHY: Simplified interface for adding single learnings without categorization.
288
+ This method wraps the batch update for convenience.
317
289
 
318
290
  Args:
319
291
  agent_id: The agent identifier
320
- learning_type: Type of learning (pattern, architecture, guideline, etc.)
321
292
  content: The learning content
322
293
 
323
294
  Returns:
324
295
  bool: True if learning was added successfully
325
296
  """
326
- section_mapping = {
327
- "pattern": "Coding Patterns Learned",
328
- "architecture": "Project Architecture",
329
- "guideline": "Implementation Guidelines",
330
- "mistake": "Common Mistakes to Avoid",
331
- "strategy": "Effective Strategies",
332
- "integration": "Integration Points",
333
- "performance": "Performance Considerations",
334
- "domain": "Domain-Specific Knowledge",
335
- "context": "Current Technical Context",
336
- }
337
-
338
- section = section_mapping.get(learning_type, "Recent Learnings")
339
- success = self.update_agent_memory(agent_id, section, content)
340
-
341
- # Socket.IO notifications removed - memory manager works independently
342
-
343
- return success
297
+ return self.update_agent_memory(agent_id, [content])
344
298
 
345
299
  def _create_default_memory(self, agent_id: str) -> str:
346
300
  """Create project-specific default memory file for agent.
@@ -492,8 +446,8 @@ class AgentMemoryManager(MemoryServiceInterface):
492
446
  """Extract memory updates from agent response and update memory file.
493
447
 
494
448
  WHY: Agents provide memory updates in their responses that need to be
495
- extracted and persisted. This method looks for "remember" field in JSON
496
- responses and merges new learnings with existing memory.
449
+ extracted and persisted. This method looks for "remember" field for incremental
450
+ updates or "MEMORIES" field for complete replacement.
497
451
 
498
452
  Args:
499
453
  agent_id: The agent identifier
@@ -517,14 +471,38 @@ class AgentMemoryManager(MemoryServiceInterface):
517
471
 
518
472
  if not json_matches:
519
473
  # Also try to find inline JSON objects
520
- json_pattern2 = r'\{[^{}]*"(?:remember|Remember)"[^{}]*\}'
474
+ json_pattern2 = r'\{[^{}]*"(?:remember|Remember|MEMORIES)"[^{}]*\}'
521
475
  json_matches = re.findall(json_pattern2, response, re.DOTALL)
522
476
 
523
477
  for json_str in json_matches:
524
478
  try:
525
479
  data = json.loads(json_str)
526
480
 
527
- # Check for memory updates in "remember" field
481
+ # Check for complete memory replacement in "MEMORIES" field
482
+ if "MEMORIES" in data and data["MEMORIES"] is not None:
483
+ memories = data["MEMORIES"]
484
+ if isinstance(memories, list) and len(memories) > 0:
485
+ # Filter out empty strings and None values
486
+ valid_items = []
487
+ for item in memories:
488
+ if item and isinstance(item, str) and item.strip():
489
+ # Ensure item has bullet point for consistency
490
+ item_text = item.strip()
491
+ if not item_text.startswith("-"):
492
+ item_text = f"- {item_text}"
493
+ valid_items.append(item_text)
494
+
495
+ if valid_items:
496
+ self.logger.info(f"Replacing all memories for {agent_id} with {len(valid_items)} items")
497
+ success = self.replace_agent_memory(agent_id, valid_items)
498
+ if success:
499
+ self.logger.info(f"Successfully replaced memories for {agent_id}")
500
+ return True
501
+ else:
502
+ self.logger.error(f"Failed to replace memories for {agent_id}")
503
+ continue # Skip checking remember field if MEMORIES was processed
504
+
505
+ # Check for incremental memory updates in "remember" field
528
506
  memory_items = None
529
507
 
530
508
  # Check both "remember" and "Remember" fields
@@ -566,11 +544,11 @@ class AgentMemoryManager(MemoryServiceInterface):
566
544
  return False
567
545
 
568
546
  def _add_learnings_to_memory(self, agent_id: str, learnings: List[str]) -> bool:
569
- """Add new learnings to existing agent memory.
547
+ """Add new learnings to agent memory as a simple list.
570
548
 
571
- WHY: Instead of replacing all memory, we want to intelligently merge new
572
- learnings with existing knowledge, avoiding duplicates and maintaining
573
- the most relevant information. PM memories are always saved to user dir.
549
+ WHY: Simplified memory system - all memories are stored as a simple list
550
+ without categorization, making it easier to manage and understand.
551
+ Updates timestamp on every update.
574
552
 
575
553
  Args:
576
554
  agent_id: The agent identifier
@@ -583,13 +561,14 @@ class AgentMemoryManager(MemoryServiceInterface):
583
561
  # Load existing memory
584
562
  current_memory = self.load_agent_memory(agent_id)
585
563
 
586
- # Parse existing memory into sections
587
- sections = self._parse_memory_sections(current_memory)
564
+ # Parse existing memory into a simple list
565
+ existing_items = self._parse_memory_list(current_memory)
588
566
 
589
- # Clean sections - remove template placeholder text
590
- sections = self._clean_template_placeholders(sections)
567
+ # Clean template placeholders if this is a fresh memory
568
+ existing_items = self._clean_template_placeholders_list(existing_items)
591
569
 
592
- # Determine which section to add learnings to based on content
570
+ # Add new learnings, avoiding duplicates
571
+ updated = False
593
572
  for learning in learnings:
594
573
  if not learning or not isinstance(learning, str):
595
574
  continue
@@ -598,35 +577,34 @@ class AgentMemoryManager(MemoryServiceInterface):
598
577
  if not learning:
599
578
  continue
600
579
 
601
- # Categorize the learning based on keywords
602
- section = self._categorize_learning(learning)
603
-
604
- # Add to appropriate section if not duplicate
605
- if section not in sections:
606
- sections[section] = []
607
-
608
- # Check for duplicates (case-insensitive) - FIXED LOGIC
580
+ # Check for duplicates (case-insensitive)
609
581
  normalized_learning = learning.lower()
610
582
  # Strip bullet points from existing items for comparison
611
- existing_normalized = [item.lstrip('- ').strip().lower() for item in sections[section]]
583
+ existing_normalized = [item.lstrip('- ').strip().lower() for item in existing_items]
612
584
 
613
585
  if normalized_learning not in existing_normalized:
614
586
  # Add bullet point if not present
615
587
  if not learning.startswith("-"):
616
588
  learning = f"- {learning}"
617
- sections[section].append(learning)
589
+ existing_items.append(learning)
618
590
  self.logger.info(f"Added new memory for {agent_id}: {learning[:50]}...")
591
+ updated = True
619
592
  else:
620
593
  self.logger.debug(f"Skipping duplicate memory for {agent_id}: {learning}")
621
594
 
622
- # Rebuild memory content
623
- new_content = self._build_memory_content(agent_id, sections)
595
+ # Only save if we actually added new items
596
+ if not updated:
597
+ self.logger.debug(f"No new memories to add for {agent_id}")
598
+ return True # Not an error, just nothing new to add
599
+
600
+ # Rebuild memory content as simple list with updated timestamp
601
+ new_content = self._build_simple_memory_content(agent_id, existing_items)
624
602
 
625
603
  # Validate and save
626
604
  agent_limits = self._get_agent_limits(agent_id)
627
605
  if self.content_manager.exceeds_limits(new_content, agent_limits):
628
606
  self.logger.debug(f"Memory for {agent_id} exceeds limits, truncating")
629
- new_content = self.content_manager.truncate_to_limits(new_content, agent_limits)
607
+ new_content = self.content_manager.truncate_simple_list(new_content, agent_limits)
630
608
 
631
609
  # All memories go to project directory
632
610
  return self._save_memory_file(agent_id, new_content)
@@ -635,6 +613,59 @@ class AgentMemoryManager(MemoryServiceInterface):
635
613
  self.logger.error(f"Error adding learnings to memory for {agent_id}: {e}")
636
614
  return False
637
615
 
616
+ def _parse_memory_list(self, memory_content: str) -> List[str]:
617
+ """Parse memory content into a simple list.
618
+
619
+ Args:
620
+ memory_content: Raw memory file content
621
+
622
+ Returns:
623
+ List of memory items
624
+ """
625
+ items = []
626
+
627
+ for line in memory_content.split('\n'):
628
+ line = line.strip()
629
+ # Skip metadata lines and headers
630
+ if line.startswith('<!-- ') or line.startswith('#') or not line:
631
+ continue
632
+ # Collect items (with or without bullet points)
633
+ if line.startswith('- '):
634
+ items.append(line)
635
+ elif line and not line.startswith('##'): # Legacy format without bullets
636
+ items.append(f"- {line}")
637
+
638
+ return items
639
+
640
+ def _clean_template_placeholders_list(self, items: List[str]) -> List[str]:
641
+ """Remove template placeholder text from item list.
642
+
643
+ Args:
644
+ items: List of memory items
645
+
646
+ Returns:
647
+ List with placeholder text removed
648
+ """
649
+ # Template placeholder patterns to remove
650
+ placeholders = [
651
+ "Analyze project structure to understand architecture patterns",
652
+ "Observe codebase patterns and conventions during tasks",
653
+ "Extract implementation guidelines from project documentation",
654
+ "Learn from errors encountered during project work",
655
+ "Project analysis pending - gather context during tasks",
656
+ "claude-mpm: Software project requiring analysis"
657
+ ]
658
+
659
+ cleaned = []
660
+ for item in items:
661
+ # Remove bullet point for comparison
662
+ item_text = item.lstrip("- ").strip()
663
+ # Keep item if it's not a placeholder
664
+ if item_text and item_text not in placeholders:
665
+ cleaned.append(item)
666
+
667
+ return cleaned
668
+
638
669
  def _clean_template_placeholders(self, sections: Dict[str, List[str]]) -> Dict[str, List[str]]:
639
670
  """Remove template placeholder text from sections.
640
671
 
@@ -723,12 +754,12 @@ class AgentMemoryManager(MemoryServiceInterface):
723
754
  else:
724
755
  return "Recent Learnings"
725
756
 
726
- def _build_memory_content(self, agent_id: str, sections: Dict[str, List[str]]) -> str:
727
- """Build memory content from sections.
757
+ def _build_simple_memory_content(self, agent_id: str, items: List[str]) -> str:
758
+ """Build memory content as a simple list with updated timestamp.
728
759
 
729
760
  Args:
730
761
  agent_id: The agent identifier
731
- sections: Dict mapping section names to lists of items
762
+ items: List of memory items
732
763
 
733
764
  Returns:
734
765
  str: The formatted memory content
@@ -736,119 +767,45 @@ class AgentMemoryManager(MemoryServiceInterface):
736
767
  lines = []
737
768
 
738
769
  # Add header
739
- lines.append(f"# {agent_id.capitalize()} Agent Memory")
770
+ lines.append(f"# Agent Memory: {agent_id}")
771
+ # Always update timestamp when building new content
772
+ lines.append(f"<!-- Last Updated: {datetime.now().isoformat()}Z -->")
740
773
  lines.append("")
741
- lines.append(f"<!-- Last Updated: {datetime.now().isoformat()} -->")
742
- lines.append("")
743
-
744
- # Add sections in consistent order
745
- section_order = [
746
- "Project Architecture",
747
- "Implementation Guidelines",
748
- "Common Mistakes to Avoid",
749
- "Current Technical Context",
750
- "Coding Patterns Learned",
751
- "Effective Strategies",
752
- "Integration Points",
753
- "Performance Considerations",
754
- "Domain-Specific Knowledge",
755
- "Recent Learnings"
756
- ]
757
-
758
- for section_name in section_order:
759
- if section_name in sections and sections[section_name]:
760
- lines.append(f"## {section_name}")
761
- lines.append("")
762
- for item in sections[section_name]:
763
- if item.strip():
764
- lines.append(item)
765
- lines.append("")
766
774
 
767
- # Add any remaining sections
768
- remaining = set(sections.keys()) - set(section_order)
769
- for section_name in sorted(remaining):
770
- if sections[section_name]:
771
- lines.append(f"## {section_name}")
772
- lines.append("")
773
- for item in sections[section_name]:
774
- if item.strip():
775
- lines.append(item)
776
- lines.append("")
775
+ # Add all items as a simple list
776
+ for item in items:
777
+ if item.strip():
778
+ # Ensure item has bullet point
779
+ if not item.strip().startswith("-"):
780
+ lines.append(f"- {item.strip()}")
781
+ else:
782
+ lines.append(item.strip())
777
783
 
778
784
  return '\n'.join(lines)
779
785
 
780
- def replace_agent_memory(self, agent_id: str, memory_sections: Dict[str, List[str]]) -> bool:
781
- """Replace agent's memory with new content organized by sections.
786
+ def replace_agent_memory(self, agent_id: str, memory_items: List[str]) -> bool:
787
+ """Replace agent's memory with new content as a simple list.
782
788
 
783
- WHY: When agents provide memory updates, they replace the existing memory
784
- rather than appending to it. This ensures memories stay current and relevant.
789
+ WHY: When agents provide complete memory updates through MEMORIES field,
790
+ they replace the existing memory rather than appending to it.
791
+ This ensures memories stay current and relevant.
785
792
 
786
793
  Args:
787
794
  agent_id: The agent identifier
788
- memory_sections: Dict mapping section names to lists of memory items
795
+ memory_items: List of memory items to replace existing memories
789
796
 
790
797
  Returns:
791
798
  bool: True if memory was successfully replaced
792
799
  """
793
800
  try:
794
- # Build new memory content
795
- lines = []
796
-
797
- # Add header
798
- lines.append(f"# {agent_id.capitalize()} Agent Memory")
799
- lines.append("")
800
- lines.append(f"<!-- Last Updated: {datetime.now().isoformat()} -->")
801
- lines.append("")
802
-
803
- # Add sections in a consistent order
804
- section_order = [
805
- "Project Architecture",
806
- "Implementation Guidelines",
807
- "Common Mistakes to Avoid",
808
- "Current Technical Context",
809
- "Coding Patterns Learned",
810
- "Effective Strategies",
811
- "Integration Points",
812
- "Performance Considerations",
813
- "Domain-Specific Knowledge",
814
- "Recent Learnings"
815
- ]
816
-
817
- # First add ordered sections that exist in memory_sections
818
- for section_name in section_order:
819
- if section_name in memory_sections and memory_sections[section_name]:
820
- lines.append(f"## {section_name}")
821
- lines.append("")
822
- for item in memory_sections[section_name]:
823
- if item.strip(): # Skip empty items
824
- # Add bullet point if not already present
825
- if not item.strip().startswith("-"):
826
- lines.append(f"- {item.strip()}")
827
- else:
828
- lines.append(item.strip())
829
- lines.append("")
830
-
831
- # Then add any remaining sections not in the order list
832
- remaining_sections = set(memory_sections.keys()) - set(section_order)
833
- for section_name in sorted(remaining_sections):
834
- if memory_sections[section_name]:
835
- lines.append(f"## {section_name}")
836
- lines.append("")
837
- for item in memory_sections[section_name]:
838
- if item.strip():
839
- if not item.strip().startswith("-"):
840
- lines.append(f"- {item.strip()}")
841
- else:
842
- lines.append(item.strip())
843
- lines.append("")
844
-
845
- new_content = '\n'.join(lines)
801
+ # Build new memory content as simple list with updated timestamp
802
+ new_content = self._build_simple_memory_content(agent_id, memory_items)
846
803
 
847
804
  # Validate and save
848
805
  agent_limits = self._get_agent_limits(agent_id)
849
806
  if self.content_manager.exceeds_limits(new_content, agent_limits):
850
807
  self.logger.debug(f"Memory for {agent_id} exceeds limits, truncating")
851
- new_content = self.content_manager.truncate_to_limits(new_content, agent_limits)
808
+ new_content = self.content_manager.truncate_simple_list(new_content, agent_limits)
852
809
 
853
810
  # Save the new memory
854
811
  return self._save_memory_file(agent_id, new_content)