claude-mpm 4.18.0__py3-none-any.whl → 4.18.3__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.
Potentially problematic release.
This version of claude-mpm might be problematic. Click here for more details.
- claude_mpm/VERSION +1 -1
- claude_mpm/agents/BASE_ENGINEER.md +286 -0
- claude_mpm/agents/templates/engineer.json +5 -1
- claude_mpm/core/config.py +42 -0
- claude_mpm/services/agents/deployment/agent_validator.py +17 -1
- claude_mpm/services/cli/session_manager.py +87 -0
- {claude_mpm-4.18.0.dist-info → claude_mpm-4.18.3.dist-info}/METADATA +1 -1
- {claude_mpm-4.18.0.dist-info → claude_mpm-4.18.3.dist-info}/RECORD +12 -12
- {claude_mpm-4.18.0.dist-info → claude_mpm-4.18.3.dist-info}/WHEEL +0 -0
- {claude_mpm-4.18.0.dist-info → claude_mpm-4.18.3.dist-info}/entry_points.txt +0 -0
- {claude_mpm-4.18.0.dist-info → claude_mpm-4.18.3.dist-info}/licenses/LICENSE +0 -0
- {claude_mpm-4.18.0.dist-info → claude_mpm-4.18.3.dist-info}/top_level.txt +0 -0
claude_mpm/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
4.18.
|
|
1
|
+
4.18.3
|
|
@@ -262,6 +262,292 @@ Before writing ANY fix or optimization, you MUST:
|
|
|
262
262
|
- **Test Coverage**: Minimum 80% for new code
|
|
263
263
|
- **Documentation**: All public APIs must have docstrings
|
|
264
264
|
|
|
265
|
+
## Engineering Quality Documentation Standards
|
|
266
|
+
|
|
267
|
+
All engineers must provide comprehensive documentation for implementations. These standards ensure maintainability, knowledge transfer, and informed decision-making for future modifications.
|
|
268
|
+
|
|
269
|
+
### Design Decision Documentation (MANDATORY)
|
|
270
|
+
|
|
271
|
+
Every significant implementation must document:
|
|
272
|
+
|
|
273
|
+
**Architectural Choices and Reasoning**
|
|
274
|
+
- Explain WHY you chose this approach over alternatives
|
|
275
|
+
- Document the problem context that influenced the decision
|
|
276
|
+
- Link design to business requirements or technical constraints
|
|
277
|
+
|
|
278
|
+
**Alternatives Considered**
|
|
279
|
+
- List other approaches evaluated during design
|
|
280
|
+
- Explain why each alternative was rejected
|
|
281
|
+
- Note any assumptions that might invalidate the current choice
|
|
282
|
+
|
|
283
|
+
**Trade-offs Analysis**
|
|
284
|
+
- **Performance vs. Maintainability**: Document speed vs. readability choices
|
|
285
|
+
- **Complexity vs. Flexibility**: Note when simplicity was chosen over extensibility
|
|
286
|
+
- **Memory vs. Speed**: Explain resource allocation decisions
|
|
287
|
+
- **Time vs. Quality**: Acknowledge technical debt taken for deadlines
|
|
288
|
+
|
|
289
|
+
**Future Extensibility**
|
|
290
|
+
- Identify extension points for anticipated changes
|
|
291
|
+
- Document which parts are designed to be stable vs. flexible
|
|
292
|
+
- Note refactoring opportunities for future consideration
|
|
293
|
+
|
|
294
|
+
**Example**:
|
|
295
|
+
```python
|
|
296
|
+
class CacheManager:
|
|
297
|
+
"""
|
|
298
|
+
Design Decision: In-memory LRU cache with TTL
|
|
299
|
+
|
|
300
|
+
Rationale: Selected in-memory caching for sub-millisecond access times
|
|
301
|
+
required by API SLA (<50ms p99 latency). Rejected Redis to avoid
|
|
302
|
+
network latency and operational complexity for this use case.
|
|
303
|
+
|
|
304
|
+
Trade-offs:
|
|
305
|
+
- Performance: O(1) access vs. Redis ~1-2ms network round-trip
|
|
306
|
+
- Scalability: Limited to single-node memory vs. distributed cache
|
|
307
|
+
- Persistence: Loses cache on restart vs. Redis durability
|
|
308
|
+
|
|
309
|
+
Alternatives Considered:
|
|
310
|
+
1. Redis: Rejected due to network latency and ops overhead
|
|
311
|
+
2. SQLite: Rejected due to disk I/O bottleneck on writes
|
|
312
|
+
3. No caching: Rejected due to database query load (2000+ QPS)
|
|
313
|
+
|
|
314
|
+
Extension Points: Cache backend interface allows future Redis migration
|
|
315
|
+
if horizontal scaling becomes necessary (>10K QPS threshold).
|
|
316
|
+
"""
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
### Performance Analysis (RECOMMENDED)
|
|
320
|
+
|
|
321
|
+
For algorithms and critical paths, provide:
|
|
322
|
+
|
|
323
|
+
**Complexity Analysis**
|
|
324
|
+
- **Time Complexity**: Big-O notation for all operations
|
|
325
|
+
- Best case, average case, worst case
|
|
326
|
+
- Explain what factors influence complexity
|
|
327
|
+
- **Space Complexity**: Memory usage characteristics
|
|
328
|
+
- Auxiliary space requirements
|
|
329
|
+
- Scalability limits based on input size
|
|
330
|
+
|
|
331
|
+
**Performance Metrics**
|
|
332
|
+
- Expected performance for typical workloads
|
|
333
|
+
- Benchmarks for critical operations
|
|
334
|
+
- Comparison to previous implementation (if refactoring)
|
|
335
|
+
|
|
336
|
+
**Bottleneck Identification**
|
|
337
|
+
- Known performance limitations
|
|
338
|
+
- Conditions that trigger worst-case behavior
|
|
339
|
+
- Scalability ceilings and their causes
|
|
340
|
+
|
|
341
|
+
**Example**:
|
|
342
|
+
```python
|
|
343
|
+
def binary_search(arr: list, target: int) -> int:
|
|
344
|
+
"""
|
|
345
|
+
Find target in sorted array using binary search.
|
|
346
|
+
|
|
347
|
+
Performance:
|
|
348
|
+
- Time Complexity: O(log n) average/worst case, O(1) best case
|
|
349
|
+
- Space Complexity: O(1) iterative implementation
|
|
350
|
+
|
|
351
|
+
Expected Performance:
|
|
352
|
+
- 1M elements: ~20 comparisons maximum
|
|
353
|
+
- 1B elements: ~30 comparisons maximum
|
|
354
|
+
|
|
355
|
+
Bottleneck: Array must be pre-sorted. If frequent insertions/deletions,
|
|
356
|
+
consider balanced tree structure (O(log n) insert vs. O(n) array insert).
|
|
357
|
+
"""
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
### Optimization Suggestions (RECOMMENDED)
|
|
361
|
+
|
|
362
|
+
Document future improvement opportunities:
|
|
363
|
+
|
|
364
|
+
**Potential Performance Improvements**
|
|
365
|
+
- Specific optimizations not yet implemented
|
|
366
|
+
- Conditions under which optimization becomes worthwhile
|
|
367
|
+
- Estimated performance gains if implemented
|
|
368
|
+
|
|
369
|
+
**Refactoring Opportunities**
|
|
370
|
+
- Code structure improvements identified during implementation
|
|
371
|
+
- Dependencies that could be reduced or eliminated
|
|
372
|
+
- Patterns that could be extracted for reuse
|
|
373
|
+
|
|
374
|
+
**Technical Debt Documentation**
|
|
375
|
+
- Shortcuts taken with explanation and remediation plan
|
|
376
|
+
- Areas needing cleanup or modernization
|
|
377
|
+
- Test coverage gaps and plan to address
|
|
378
|
+
|
|
379
|
+
**Scalability Considerations**
|
|
380
|
+
- Current capacity limits and how to exceed them
|
|
381
|
+
- Architectural changes needed for 10x/100x scale
|
|
382
|
+
- Resource utilization projections
|
|
383
|
+
|
|
384
|
+
**Example**:
|
|
385
|
+
```python
|
|
386
|
+
class ReportGenerator:
|
|
387
|
+
"""
|
|
388
|
+
Current Implementation: Synchronous PDF generation
|
|
389
|
+
|
|
390
|
+
Optimization Opportunities:
|
|
391
|
+
1. Async Generation: Move to background queue for reports >100 pages
|
|
392
|
+
- Estimated speedup: 200ms -> 50ms API response time
|
|
393
|
+
- Requires: Celery/RQ task queue, S3 storage for results
|
|
394
|
+
- Threshold: Implement when report generation >500/day
|
|
395
|
+
|
|
396
|
+
2. Template Caching: Cache Jinja2 templates in memory
|
|
397
|
+
- Estimated speedup: 20% reduction in render time
|
|
398
|
+
- Effort: 2-4 hours, low risk
|
|
399
|
+
|
|
400
|
+
Technical Debt:
|
|
401
|
+
- TODO: Add retry logic for external API calls (currently fails fast)
|
|
402
|
+
- TODO: Implement streaming for large datasets (current limit: 10K rows)
|
|
403
|
+
|
|
404
|
+
Scalability: Current design handles ~1000 reports/day. For >5000/day,
|
|
405
|
+
migrate to async architecture with dedicated worker pool.
|
|
406
|
+
"""
|
|
407
|
+
```
|
|
408
|
+
|
|
409
|
+
### Error Case Documentation (MANDATORY)
|
|
410
|
+
|
|
411
|
+
Every implementation must document failure modes:
|
|
412
|
+
|
|
413
|
+
**All Error Conditions Handled**
|
|
414
|
+
- List every exception caught and why
|
|
415
|
+
- Document error recovery strategies
|
|
416
|
+
- Explain error propagation decisions (catch vs. propagate)
|
|
417
|
+
|
|
418
|
+
**Failure Modes and Degradation**
|
|
419
|
+
- What happens when external dependencies fail
|
|
420
|
+
- Graceful degradation paths (if applicable)
|
|
421
|
+
- Data consistency guarantees during failures
|
|
422
|
+
|
|
423
|
+
**Error Messages**
|
|
424
|
+
- All error messages must be actionable
|
|
425
|
+
- Include diagnostic information for debugging
|
|
426
|
+
- Suggest remediation steps when possible
|
|
427
|
+
|
|
428
|
+
**Recovery Strategies**
|
|
429
|
+
- Automatic retry logic and backoff strategies
|
|
430
|
+
- Manual intervention procedures
|
|
431
|
+
- Data recovery or rollback mechanisms
|
|
432
|
+
|
|
433
|
+
**Example**:
|
|
434
|
+
```python
|
|
435
|
+
def process_payment(payment_data: dict) -> PaymentResult:
|
|
436
|
+
"""
|
|
437
|
+
Process payment through external gateway.
|
|
438
|
+
|
|
439
|
+
Error Handling:
|
|
440
|
+
1. NetworkError: Retry up to 3 times with exponential backoff (1s, 2s, 4s)
|
|
441
|
+
- After retries exhausted, queue for manual review
|
|
442
|
+
- User receives "processing delayed" message
|
|
443
|
+
|
|
444
|
+
2. ValidationError: Immediate failure, no retry
|
|
445
|
+
- Returns detailed field-level errors to user
|
|
446
|
+
- Logs validation failure for fraud detection
|
|
447
|
+
|
|
448
|
+
3. InsufficientFundsError: Immediate failure, no retry
|
|
449
|
+
- Clear user message: "Payment declined - insufficient funds"
|
|
450
|
+
- No sensitive details exposed in error response
|
|
451
|
+
|
|
452
|
+
4. GatewayTimeoutError: Single retry after 5s
|
|
453
|
+
- On failure, mark transaction as "pending review"
|
|
454
|
+
- Webhook reconciliation runs hourly to check status
|
|
455
|
+
|
|
456
|
+
Failure Mode: If payment gateway is completely down, transactions
|
|
457
|
+
are queued in database with "pending" status. Background worker
|
|
458
|
+
processes queue every 5 minutes. Users notified of delay via email.
|
|
459
|
+
|
|
460
|
+
Data Consistency: Transaction state transitions are atomic. No partial
|
|
461
|
+
payments possible. Database transaction wraps payment + order update.
|
|
462
|
+
"""
|
|
463
|
+
```
|
|
464
|
+
|
|
465
|
+
### Usage Examples (RECOMMENDED)
|
|
466
|
+
|
|
467
|
+
Provide practical code examples:
|
|
468
|
+
|
|
469
|
+
**Common Use Cases**
|
|
470
|
+
- Show typical usage patterns for APIs
|
|
471
|
+
- Include complete, runnable examples
|
|
472
|
+
- Demonstrate best practices
|
|
473
|
+
|
|
474
|
+
**Edge Case Handling**
|
|
475
|
+
- Show how to handle boundary conditions
|
|
476
|
+
- Demonstrate error handling in practice
|
|
477
|
+
- Illustrate performance considerations
|
|
478
|
+
|
|
479
|
+
**Integration Examples**
|
|
480
|
+
- How to use with other system components
|
|
481
|
+
- Configuration examples
|
|
482
|
+
- Dependency setup instructions
|
|
483
|
+
|
|
484
|
+
**Test Case References**
|
|
485
|
+
- Point to test files demonstrating usage
|
|
486
|
+
- Explain what each test validates
|
|
487
|
+
- Use tests as living documentation
|
|
488
|
+
|
|
489
|
+
**Example**:
|
|
490
|
+
```python
|
|
491
|
+
class DataValidator:
|
|
492
|
+
"""
|
|
493
|
+
Validate user input against schema definitions.
|
|
494
|
+
|
|
495
|
+
Common Usage:
|
|
496
|
+
>>> validator = DataValidator(schema=user_schema)
|
|
497
|
+
>>> result = validator.validate(user_data)
|
|
498
|
+
>>> if result.is_valid:
|
|
499
|
+
>>> process_user(result.cleaned_data)
|
|
500
|
+
>>> else:
|
|
501
|
+
>>> return {"errors": result.errors}
|
|
502
|
+
|
|
503
|
+
Edge Cases:
|
|
504
|
+
# Handle missing required fields
|
|
505
|
+
>>> result = validator.validate({})
|
|
506
|
+
>>> result.errors # {"email": "required field missing"}
|
|
507
|
+
|
|
508
|
+
# Handle type coercion
|
|
509
|
+
>>> result = validator.validate({"age": "25"})
|
|
510
|
+
>>> result.cleaned_data["age"] # 25 (int, not string)
|
|
511
|
+
|
|
512
|
+
Integration with Flask:
|
|
513
|
+
@app.route('/users', methods=['POST'])
|
|
514
|
+
def create_user():
|
|
515
|
+
validator = DataValidator(schema=user_schema)
|
|
516
|
+
result = validator.validate(request.json)
|
|
517
|
+
if not result.is_valid:
|
|
518
|
+
return jsonify({"errors": result.errors}), 400
|
|
519
|
+
# ... process valid data
|
|
520
|
+
|
|
521
|
+
Tests: See tests/test_validators.py for comprehensive examples
|
|
522
|
+
- test_required_fields: Required field validation
|
|
523
|
+
- test_type_coercion: Automatic type conversion
|
|
524
|
+
- test_custom_validators: Custom validation rules
|
|
525
|
+
"""
|
|
526
|
+
```
|
|
527
|
+
|
|
528
|
+
## Documentation Enforcement
|
|
529
|
+
|
|
530
|
+
**Mandatory Reviews**
|
|
531
|
+
- Code reviews must verify documentation completeness
|
|
532
|
+
- PRs without proper documentation must be rejected
|
|
533
|
+
- Design decisions require explicit approval
|
|
534
|
+
|
|
535
|
+
**Documentation Quality Checks**
|
|
536
|
+
- MANDATORY sections must be present and complete
|
|
537
|
+
- RECOMMENDED sections encouraged but not blocking
|
|
538
|
+
- Examples must be runnable and tested
|
|
539
|
+
- Error cases must cover all catch/except blocks
|
|
540
|
+
|
|
541
|
+
**Success Criteria**
|
|
542
|
+
- ✅ Design rationale clearly explained
|
|
543
|
+
- ✅ Trade-offs explicitly documented
|
|
544
|
+
- ✅ All error conditions documented
|
|
545
|
+
- ✅ At least one usage example provided
|
|
546
|
+
- ✅ Complexity analysis for non-trivial algorithms
|
|
547
|
+
- ❌ "Self-documenting code" without explanation
|
|
548
|
+
- ❌ Generic/copied docstring templates
|
|
549
|
+
- ❌ Undocumented error handling
|
|
550
|
+
|
|
265
551
|
### Implementation Patterns
|
|
266
552
|
|
|
267
553
|
#### Technical Patterns
|
|
@@ -112,7 +112,11 @@
|
|
|
112
112
|
"Plan modularization at 600 lines",
|
|
113
113
|
"Review file commit history before modifications: git log --oneline -5 <file_path>",
|
|
114
114
|
"Write succinct commit messages explaining WHAT changed and WHY",
|
|
115
|
-
"Follow conventional commits format: feat/fix/docs/refactor/perf/test/chore"
|
|
115
|
+
"Follow conventional commits format: feat/fix/docs/refactor/perf/test/chore",
|
|
116
|
+
"Document design decisions and architectural trade-offs",
|
|
117
|
+
"Provide complexity analysis (time/space) for algorithms",
|
|
118
|
+
"Include practical usage examples in documentation",
|
|
119
|
+
"Document all error cases and failure modes"
|
|
116
120
|
],
|
|
117
121
|
"constraints": [],
|
|
118
122
|
"examples": []
|
claude_mpm/core/config.py
CHANGED
|
@@ -578,6 +578,11 @@ class Config:
|
|
|
578
578
|
"[PM-REMINDER] Your role is coordination and management",
|
|
579
579
|
],
|
|
580
580
|
},
|
|
581
|
+
# Session management configuration
|
|
582
|
+
"session": {
|
|
583
|
+
"auto_save": True, # Enable automatic session saving
|
|
584
|
+
"save_interval": 300, # Auto-save interval in seconds (5 minutes)
|
|
585
|
+
},
|
|
581
586
|
}
|
|
582
587
|
|
|
583
588
|
# Apply defaults for missing keys
|
|
@@ -588,6 +593,9 @@ class Config:
|
|
|
588
593
|
# Validate health and recovery configuration
|
|
589
594
|
self._validate_health_recovery_config()
|
|
590
595
|
|
|
596
|
+
# Validate session configuration
|
|
597
|
+
self._validate_session_config()
|
|
598
|
+
|
|
591
599
|
def get(self, key: str, default: Any = None) -> Any:
|
|
592
600
|
"""Get configuration value."""
|
|
593
601
|
# Support nested keys with dot notation
|
|
@@ -764,6 +772,40 @@ class Config:
|
|
|
764
772
|
except Exception as e:
|
|
765
773
|
logger.error(f"Error validating health/recovery configuration: {e}")
|
|
766
774
|
|
|
775
|
+
def _validate_session_config(self) -> None:
|
|
776
|
+
"""Validate session management configuration."""
|
|
777
|
+
try:
|
|
778
|
+
session_config = self.get("session", {})
|
|
779
|
+
|
|
780
|
+
# Validate save_interval range (60-1800 seconds)
|
|
781
|
+
save_interval = session_config.get("save_interval", 300)
|
|
782
|
+
if not isinstance(save_interval, int):
|
|
783
|
+
logger.warning(
|
|
784
|
+
f"Session save_interval must be integer, got {type(save_interval).__name__}, using default 300"
|
|
785
|
+
)
|
|
786
|
+
self.set("session.save_interval", 300)
|
|
787
|
+
elif save_interval < 60:
|
|
788
|
+
logger.warning(
|
|
789
|
+
f"Session save_interval must be at least 60 seconds, got {save_interval}, using 60"
|
|
790
|
+
)
|
|
791
|
+
self.set("session.save_interval", 60)
|
|
792
|
+
elif save_interval > 1800:
|
|
793
|
+
logger.warning(
|
|
794
|
+
f"Session save_interval must be at most 1800 seconds (30 min), got {save_interval}, using 1800"
|
|
795
|
+
)
|
|
796
|
+
self.set("session.save_interval", 1800)
|
|
797
|
+
|
|
798
|
+
# Validate auto_save is boolean
|
|
799
|
+
auto_save = session_config.get("auto_save", True)
|
|
800
|
+
if not isinstance(auto_save, bool):
|
|
801
|
+
logger.warning(
|
|
802
|
+
f"Session auto_save must be boolean, got {type(auto_save).__name__}, using True"
|
|
803
|
+
)
|
|
804
|
+
self.set("session.auto_save", True)
|
|
805
|
+
|
|
806
|
+
except Exception as e:
|
|
807
|
+
logger.error(f"Error validating session configuration: {e}")
|
|
808
|
+
|
|
767
809
|
def get_health_monitoring_config(self) -> Dict[str, Any]:
|
|
768
810
|
"""Get health monitoring configuration with defaults."""
|
|
769
811
|
base_config = {
|
|
@@ -329,10 +329,26 @@ class AgentValidator:
|
|
|
329
329
|
"type": "agent", # Default type
|
|
330
330
|
}
|
|
331
331
|
|
|
332
|
-
# Extract from YAML frontmatter
|
|
332
|
+
# Extract ONLY from YAML frontmatter (between --- markers)
|
|
333
333
|
lines = content.split("\n")
|
|
334
|
+
in_frontmatter = False
|
|
335
|
+
frontmatter_ended = False
|
|
336
|
+
|
|
334
337
|
for line in lines:
|
|
335
338
|
stripped_line = line.strip()
|
|
339
|
+
|
|
340
|
+
# Track frontmatter boundaries
|
|
341
|
+
if stripped_line == "---":
|
|
342
|
+
if not in_frontmatter:
|
|
343
|
+
in_frontmatter = True
|
|
344
|
+
continue
|
|
345
|
+
frontmatter_ended = True
|
|
346
|
+
break # Stop parsing after frontmatter ends
|
|
347
|
+
|
|
348
|
+
# Only parse within frontmatter
|
|
349
|
+
if not in_frontmatter or frontmatter_ended:
|
|
350
|
+
continue
|
|
351
|
+
|
|
336
352
|
if stripped_line.startswith("name:"):
|
|
337
353
|
agent_info["name"] = stripped_line.split(":", 1)[1].strip().strip("\"'")
|
|
338
354
|
elif stripped_line.startswith("description:"):
|
|
@@ -11,8 +11,10 @@ DESIGN DECISIONS:
|
|
|
11
11
|
- Automatic session cleanup and archiving
|
|
12
12
|
- Thread-safe session operations
|
|
13
13
|
- Non-blocking validation with structured warnings
|
|
14
|
+
- Async-first design with periodic auto-save task
|
|
14
15
|
"""
|
|
15
16
|
|
|
17
|
+
import asyncio
|
|
16
18
|
import gzip
|
|
17
19
|
import json
|
|
18
20
|
import uuid
|
|
@@ -217,8 +219,39 @@ class SessionManager(ISessionManager):
|
|
|
217
219
|
self.config_service = config_service
|
|
218
220
|
self.logger = get_logger("SessionManager")
|
|
219
221
|
self._sessions_cache: Dict[str, SessionInfo] = {}
|
|
222
|
+
self._auto_save_task: Optional[asyncio.Task] = None
|
|
223
|
+
self._running = False
|
|
220
224
|
self._load_sessions()
|
|
221
225
|
|
|
226
|
+
# Start auto-save task if enabled and event loop is running
|
|
227
|
+
if config_service:
|
|
228
|
+
auto_save_enabled = config_service.get("session.auto_save", True)
|
|
229
|
+
if auto_save_enabled:
|
|
230
|
+
self._start_auto_save()
|
|
231
|
+
else:
|
|
232
|
+
self.logger.info("Auto-save disabled by configuration")
|
|
233
|
+
else:
|
|
234
|
+
self.logger.debug("No config service provided, auto-save not started")
|
|
235
|
+
|
|
236
|
+
def _start_auto_save(self) -> None:
|
|
237
|
+
"""Start the auto-save background task.
|
|
238
|
+
|
|
239
|
+
WHY: Separated from __init__ to allow safe initialization without event loop.
|
|
240
|
+
Can be called when event loop is available.
|
|
241
|
+
"""
|
|
242
|
+
try:
|
|
243
|
+
loop = asyncio.get_running_loop()
|
|
244
|
+
self._running = True
|
|
245
|
+
self._auto_save_task = loop.create_task(self._periodic_session_save())
|
|
246
|
+
self.logger.info("Auto-save task started")
|
|
247
|
+
except RuntimeError:
|
|
248
|
+
# No event loop running, schedule for later
|
|
249
|
+
self.logger.debug(
|
|
250
|
+
"No event loop running, auto-save will start when loop is available"
|
|
251
|
+
)
|
|
252
|
+
# Set flag so we know to start it later
|
|
253
|
+
self._running = True
|
|
254
|
+
|
|
222
255
|
def create_session(
|
|
223
256
|
self, context: str = "default", options: Optional[Dict[str, Any]] = None
|
|
224
257
|
) -> SessionInfo:
|
|
@@ -477,6 +510,60 @@ class SessionManager(ISessionManager):
|
|
|
477
510
|
self.logger.error(f"Failed to load sessions: {e}")
|
|
478
511
|
self._sessions_cache = {}
|
|
479
512
|
|
|
513
|
+
async def _periodic_session_save(self) -> None:
|
|
514
|
+
"""Periodically save sessions to disk.
|
|
515
|
+
|
|
516
|
+
WHY: Ensures sessions are persisted regularly to prevent data loss.
|
|
517
|
+
Follows the async pattern from EventAggregator._periodic_cleanup().
|
|
518
|
+
"""
|
|
519
|
+
if not self.config_service:
|
|
520
|
+
self.logger.warning("No config service, cannot determine save interval")
|
|
521
|
+
return
|
|
522
|
+
|
|
523
|
+
save_interval = self.config_service.get("session.save_interval", 300)
|
|
524
|
+
self.logger.info(f"Starting periodic session save (interval: {save_interval}s)")
|
|
525
|
+
|
|
526
|
+
while self._running:
|
|
527
|
+
try:
|
|
528
|
+
await asyncio.sleep(save_interval)
|
|
529
|
+
|
|
530
|
+
if self._sessions_cache:
|
|
531
|
+
self._save_sessions()
|
|
532
|
+
self.logger.debug(
|
|
533
|
+
f"Auto-saved {len(self._sessions_cache)} session(s)"
|
|
534
|
+
)
|
|
535
|
+
else:
|
|
536
|
+
self.logger.debug("No sessions to save")
|
|
537
|
+
|
|
538
|
+
except asyncio.CancelledError:
|
|
539
|
+
self.logger.info("Auto-save task cancelled")
|
|
540
|
+
break
|
|
541
|
+
except Exception as e:
|
|
542
|
+
self.logger.error(f"Error in auto-save task: {e}")
|
|
543
|
+
|
|
544
|
+
async def cleanup(self) -> None:
|
|
545
|
+
"""Clean up resources and stop background tasks.
|
|
546
|
+
|
|
547
|
+
WHY: Ensures graceful shutdown of the SessionManager and all background tasks.
|
|
548
|
+
"""
|
|
549
|
+
self.logger.info("Shutting down SessionManager...")
|
|
550
|
+
self._running = False
|
|
551
|
+
|
|
552
|
+
# Cancel auto-save task
|
|
553
|
+
if self._auto_save_task and not self._auto_save_task.done():
|
|
554
|
+
self._auto_save_task.cancel()
|
|
555
|
+
try:
|
|
556
|
+
await self._auto_save_task
|
|
557
|
+
except asyncio.CancelledError:
|
|
558
|
+
pass
|
|
559
|
+
|
|
560
|
+
# Final save before shutdown
|
|
561
|
+
if self._sessions_cache:
|
|
562
|
+
self._save_sessions()
|
|
563
|
+
self.logger.info(f"Final save: {len(self._sessions_cache)} session(s)")
|
|
564
|
+
|
|
565
|
+
self.logger.info("SessionManager shutdown complete")
|
|
566
|
+
|
|
480
567
|
|
|
481
568
|
# Context manager for session management
|
|
482
569
|
class ManagedSession:
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
claude_mpm/BUILD_NUMBER,sha256=9JfxhnDtr-8l3kCP2U5TVXSErptHoga8m7XA8zqgGOc,4
|
|
2
|
-
claude_mpm/VERSION,sha256=
|
|
2
|
+
claude_mpm/VERSION,sha256=Q5Zpc7PYaq2Bzb0frfdpC9P0XLSuxcM8tmuczgbBofk,7
|
|
3
3
|
claude_mpm/__init__.py,sha256=UCw6j9e_tZQ3kJtTqmdfNv7MHyw9nD1jkj80WurwM2g,2064
|
|
4
4
|
claude_mpm/__main__.py,sha256=Ro5UBWBoQaSAIoSqWAr7zkbLyvi4sSy28WShqAhKJG0,723
|
|
5
5
|
claude_mpm/constants.py,sha256=sLjJF6Kw7H4V9WWeaEYltM-77TgXqzEMX5vx4ukM5-0,5977
|
|
@@ -7,7 +7,7 @@ claude_mpm/init.py,sha256=HFJR_JmTHa53FAhbGusm3P1iXT6VR6V7S-YJz4pOZ1Q,15967
|
|
|
7
7
|
claude_mpm/ticket_wrapper.py,sha256=qe5xY579t7_7fK5nyeAfHN_fr7CXdeOD3jfXEc8-7yo,828
|
|
8
8
|
claude_mpm/agents/BASE_AGENT_TEMPLATE.md,sha256=wFqZssWaeCGaW04yy7eScYOlqJmsu1VhsA83s2X_yMU,9138
|
|
9
9
|
claude_mpm/agents/BASE_DOCUMENTATION.md,sha256=iGub94w3IRURz2PwT0YrfNegMlEzgZ9mzeWa_WBk1rA,1504
|
|
10
|
-
claude_mpm/agents/BASE_ENGINEER.md,sha256=
|
|
10
|
+
claude_mpm/agents/BASE_ENGINEER.md,sha256=I7BusSGQfuV0uSkRBro51qzCzagUEPrXnndIElypPJk,24434
|
|
11
11
|
claude_mpm/agents/BASE_OPS.md,sha256=azAjZTrj77IZIBgZxX2hcjPOQMznV0v6B4ZWNUhAT9g,7636
|
|
12
12
|
claude_mpm/agents/BASE_PM.md,sha256=4asvm0xIxstAJFqZxC-eJKGSTGmgmzRQGWcoyzCfyQY,7292
|
|
13
13
|
claude_mpm/agents/BASE_PROMPT_ENGINEER.md,sha256=DHw7KXA6Nw7RfTyQiY06B_hH6ng2meimhgUKpQp08MM,21736
|
|
@@ -40,7 +40,7 @@ claude_mpm/agents/templates/content-agent.json,sha256=KfE8ozTsLmQiJgKnX-6yH1bOhd
|
|
|
40
40
|
claude_mpm/agents/templates/dart_engineer.json,sha256=2XsrLpeL9-f94S7mq7e8Rela-W-dl2tGwksOY8S4nEo,22685
|
|
41
41
|
claude_mpm/agents/templates/data_engineer.json,sha256=BPGgVsQPdg5YHRxlSGSIfA3xBihIXGYcrWbtaeYGd6g,20028
|
|
42
42
|
claude_mpm/agents/templates/documentation.json,sha256=jdDZdz8eCW6jF-jow5l1Z6SFBHyd6t-34hNAb0bbfe4,10355
|
|
43
|
-
claude_mpm/agents/templates/engineer.json,sha256=
|
|
43
|
+
claude_mpm/agents/templates/engineer.json,sha256=1z2tXmuGfdk8_T43A__piMSHv-RK7BxwoyOMRPt8Qz8,6047
|
|
44
44
|
claude_mpm/agents/templates/gcp_ops_agent.json,sha256=HtW3m9S6FLxxoa3ATzOWB2YLuCsRdNnQ4pztuyei6Q0,11208
|
|
45
45
|
claude_mpm/agents/templates/git_file_tracking.md,sha256=r8qckL8pS2nIvZAImFgbjAzpaEhzV44B1lzz6V8Jg8M,18842
|
|
46
46
|
claude_mpm/agents/templates/golang_engineer.json,sha256=_I5G6fmQb1Qf5uIM4Q46HCujndU2Lq96V0CoH6qi3D4,12905
|
|
@@ -195,7 +195,7 @@ claude_mpm/core/api_validator.py,sha256=U2PfksS1gEwMCkBMUjsUmFVPg2coZ6mwugBE_5kw
|
|
|
195
195
|
claude_mpm/core/base_service.py,sha256=4Zrt-vQ6g_KVk4hEfvd4QccuItYoOf7wUHCimIW6zJQ,29953
|
|
196
196
|
claude_mpm/core/cache.py,sha256=orh8B40oYnRRGmXdANR4P4f-KVMIHV0vg7vrsemk0no,17232
|
|
197
197
|
claude_mpm/core/claude_runner.py,sha256=h64Nbo0eh7pfTk_QMO3tb3rQZEEULRvSfLN8vgpZXdQ,34686
|
|
198
|
-
claude_mpm/core/config.py,sha256=
|
|
198
|
+
claude_mpm/core/config.py,sha256=qBus1zl0l56XntWaElWWyiqQ4mhDDPG7o1ELUOXiK2k,41300
|
|
199
199
|
claude_mpm/core/config_aliases.py,sha256=QpNNECkTY4TYYAhVlFZvB-msPnZrui90g19u0-v0HDE,9703
|
|
200
200
|
claude_mpm/core/config_constants.py,sha256=MEF35Y2Lj5FMzRdhgSgZrZeTzHfCzistPGZVY8INta4,10073
|
|
201
201
|
claude_mpm/core/constants.py,sha256=uBCd4kUbwSFKW79pH7_yUzRpMoNY2AcAflENTkIZIaU,11558
|
|
@@ -496,7 +496,7 @@ claude_mpm/services/agents/deployment/agent_record_service.py,sha256=dNkygfKDn4V
|
|
|
496
496
|
claude_mpm/services/agents/deployment/agent_restore_handler.py,sha256=YDPVY4m_4l5wSFq9fxsTqm0ixX8AeTyzERh-PtabuUY,3101
|
|
497
497
|
claude_mpm/services/agents/deployment/agent_state_service.py,sha256=cFBv8GEdW8gmch5OlpI6zCQdv3ANEqYV1YL0VIBRp0U,11799
|
|
498
498
|
claude_mpm/services/agents/deployment/agent_template_builder.py,sha256=0apuovTjKbIPLKdo0xHXd1-ABZH5_twnErl4ooZHmFU,46004
|
|
499
|
-
claude_mpm/services/agents/deployment/agent_validator.py,sha256=
|
|
499
|
+
claude_mpm/services/agents/deployment/agent_validator.py,sha256=4kUKHotEq_3HSNMbgSDdbtxUOVuFxtbFch87qF54c7Y,13438
|
|
500
500
|
claude_mpm/services/agents/deployment/agent_version_manager.py,sha256=Q2COYmnWDvDxsBh_ikN9Govk3o8Dy1T0X-rioMpUsis,11189
|
|
501
501
|
claude_mpm/services/agents/deployment/agent_versioning.py,sha256=PrOrauoe0rUftZvNSf0Uhch9DkqY4rtDrJf2uUXX9oE,999
|
|
502
502
|
claude_mpm/services/agents/deployment/agents_directory_resolver.py,sha256=4ylwNMs9i8eGa1jSBkU0wW1O0Jvr4GNrrWX81F0wQWU,2175
|
|
@@ -577,7 +577,7 @@ claude_mpm/services/cli/agent_output_formatter.py,sha256=FcJ18m3CyeFr_wotJfEmIUR
|
|
|
577
577
|
claude_mpm/services/cli/agent_validation_service.py,sha256=QtpP9GOwCD2wW40winh5NDrYXXLiFd_CXFUBWeYiMCk,21746
|
|
578
578
|
claude_mpm/services/cli/memory_crud_service.py,sha256=tU0zegxba5lhpOCGqLijN2NfEMLwnEoYyjaF5cZ1A90,22863
|
|
579
579
|
claude_mpm/services/cli/memory_output_formatter.py,sha256=iNXMSu-K24rkgVhJURS-B6GqUBxR1XEZg8ZewOll0SU,24796
|
|
580
|
-
claude_mpm/services/cli/session_manager.py,sha256
|
|
580
|
+
claude_mpm/services/cli/session_manager.py,sha256=-zG0osFnDYyta9iPCwmL5Dxu6Z-4_LHylFfqQAnIEvY,20126
|
|
581
581
|
claude_mpm/services/cli/startup_checker.py,sha256=MmztJ7yAnk5O5shArGDzRzwBDmx1WikhtE5s9P4IU9M,12251
|
|
582
582
|
claude_mpm/services/cli/unified_dashboard_manager.py,sha256=LH45wbiKn5GqEZWPH-18TQnsisR3vQchvSnxa-_CDZI,15607
|
|
583
583
|
claude_mpm/services/communication/__init__.py,sha256=b4qc7_Rqy4DE9q7BAUlfUZjoYG4uimAyUnE0irPcXyU,560
|
|
@@ -886,9 +886,9 @@ claude_mpm/utils/subprocess_utils.py,sha256=D0izRT8anjiUb_JG72zlJR_JAw1cDkb7kalN
|
|
|
886
886
|
claude_mpm/validation/__init__.py,sha256=YZhwE3mhit-lslvRLuwfX82xJ_k4haZeKmh4IWaVwtk,156
|
|
887
887
|
claude_mpm/validation/agent_validator.py,sha256=GprtAvu80VyMXcKGsK_VhYiXWA6BjKHv7O6HKx0AB9w,20917
|
|
888
888
|
claude_mpm/validation/frontmatter_validator.py,sha256=YpJlYNNYcV8u6hIOi3_jaRsDnzhbcQpjCBE6eyBKaFY,7076
|
|
889
|
-
claude_mpm-4.18.
|
|
890
|
-
claude_mpm-4.18.
|
|
891
|
-
claude_mpm-4.18.
|
|
892
|
-
claude_mpm-4.18.
|
|
893
|
-
claude_mpm-4.18.
|
|
894
|
-
claude_mpm-4.18.
|
|
889
|
+
claude_mpm-4.18.3.dist-info/licenses/LICENSE,sha256=lpaivOlPuBZW1ds05uQLJJswy8Rp_HMNieJEbFlqvLk,1072
|
|
890
|
+
claude_mpm-4.18.3.dist-info/METADATA,sha256=Y03L0BmDQCSMbwp5WhxA8b1ZR-OjCMivxy2roQkXPPc,22476
|
|
891
|
+
claude_mpm-4.18.3.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
892
|
+
claude_mpm-4.18.3.dist-info/entry_points.txt,sha256=Vlw3GNi-OtTpKSrez04iNrPmxNxYDpIWxmJCxiZ5Tx8,526
|
|
893
|
+
claude_mpm-4.18.3.dist-info/top_level.txt,sha256=1nUg3FEaBySgm8t-s54jK5zoPnu3_eY6EP6IOlekyHA,11
|
|
894
|
+
claude_mpm-4.18.3.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|