alma-memory 0.5.1__py3-none-any.whl → 0.7.0__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.
Files changed (111) hide show
  1. alma/__init__.py +296 -226
  2. alma/compression/__init__.py +33 -0
  3. alma/compression/pipeline.py +980 -0
  4. alma/confidence/__init__.py +47 -47
  5. alma/confidence/engine.py +540 -540
  6. alma/confidence/types.py +351 -351
  7. alma/config/loader.py +157 -157
  8. alma/consolidation/__init__.py +23 -23
  9. alma/consolidation/engine.py +678 -678
  10. alma/consolidation/prompts.py +84 -84
  11. alma/core.py +1189 -430
  12. alma/domains/__init__.py +30 -30
  13. alma/domains/factory.py +359 -359
  14. alma/domains/schemas.py +448 -448
  15. alma/domains/types.py +272 -272
  16. alma/events/__init__.py +75 -75
  17. alma/events/emitter.py +285 -284
  18. alma/events/storage_mixin.py +246 -246
  19. alma/events/types.py +126 -126
  20. alma/events/webhook.py +425 -425
  21. alma/exceptions.py +49 -49
  22. alma/extraction/__init__.py +31 -31
  23. alma/extraction/auto_learner.py +265 -265
  24. alma/extraction/extractor.py +420 -420
  25. alma/graph/__init__.py +106 -106
  26. alma/graph/backends/__init__.py +32 -32
  27. alma/graph/backends/kuzu.py +624 -624
  28. alma/graph/backends/memgraph.py +432 -432
  29. alma/graph/backends/memory.py +236 -236
  30. alma/graph/backends/neo4j.py +417 -417
  31. alma/graph/base.py +159 -159
  32. alma/graph/extraction.py +198 -198
  33. alma/graph/store.py +860 -860
  34. alma/harness/__init__.py +35 -35
  35. alma/harness/base.py +386 -386
  36. alma/harness/domains.py +705 -705
  37. alma/initializer/__init__.py +37 -37
  38. alma/initializer/initializer.py +418 -418
  39. alma/initializer/types.py +250 -250
  40. alma/integration/__init__.py +62 -62
  41. alma/integration/claude_agents.py +444 -444
  42. alma/integration/helena.py +423 -423
  43. alma/integration/victor.py +471 -471
  44. alma/learning/__init__.py +101 -86
  45. alma/learning/decay.py +878 -0
  46. alma/learning/forgetting.py +1446 -1446
  47. alma/learning/heuristic_extractor.py +390 -390
  48. alma/learning/protocols.py +374 -374
  49. alma/learning/validation.py +346 -346
  50. alma/mcp/__init__.py +123 -45
  51. alma/mcp/__main__.py +156 -156
  52. alma/mcp/resources.py +122 -122
  53. alma/mcp/server.py +955 -591
  54. alma/mcp/tools.py +3254 -509
  55. alma/observability/__init__.py +91 -84
  56. alma/observability/config.py +302 -302
  57. alma/observability/guidelines.py +170 -0
  58. alma/observability/logging.py +424 -424
  59. alma/observability/metrics.py +583 -583
  60. alma/observability/tracing.py +440 -440
  61. alma/progress/__init__.py +21 -21
  62. alma/progress/tracker.py +607 -607
  63. alma/progress/types.py +250 -250
  64. alma/retrieval/__init__.py +134 -53
  65. alma/retrieval/budget.py +525 -0
  66. alma/retrieval/cache.py +1304 -1061
  67. alma/retrieval/embeddings.py +202 -202
  68. alma/retrieval/engine.py +850 -427
  69. alma/retrieval/modes.py +365 -0
  70. alma/retrieval/progressive.py +560 -0
  71. alma/retrieval/scoring.py +344 -344
  72. alma/retrieval/trust_scoring.py +637 -0
  73. alma/retrieval/verification.py +797 -0
  74. alma/session/__init__.py +19 -19
  75. alma/session/manager.py +442 -399
  76. alma/session/types.py +288 -288
  77. alma/storage/__init__.py +101 -90
  78. alma/storage/archive.py +233 -0
  79. alma/storage/azure_cosmos.py +1259 -1259
  80. alma/storage/base.py +1083 -583
  81. alma/storage/chroma.py +1443 -1443
  82. alma/storage/constants.py +103 -103
  83. alma/storage/file_based.py +614 -614
  84. alma/storage/migrations/__init__.py +21 -21
  85. alma/storage/migrations/base.py +321 -321
  86. alma/storage/migrations/runner.py +323 -323
  87. alma/storage/migrations/version_stores.py +337 -337
  88. alma/storage/migrations/versions/__init__.py +11 -11
  89. alma/storage/migrations/versions/v1_0_0.py +373 -373
  90. alma/storage/migrations/versions/v1_1_0_workflow_context.py +551 -0
  91. alma/storage/pinecone.py +1080 -1080
  92. alma/storage/postgresql.py +1948 -1559
  93. alma/storage/qdrant.py +1306 -1306
  94. alma/storage/sqlite_local.py +3041 -1457
  95. alma/testing/__init__.py +46 -46
  96. alma/testing/factories.py +301 -301
  97. alma/testing/mocks.py +389 -389
  98. alma/types.py +292 -264
  99. alma/utils/__init__.py +19 -0
  100. alma/utils/tokenizer.py +521 -0
  101. alma/workflow/__init__.py +83 -0
  102. alma/workflow/artifacts.py +170 -0
  103. alma/workflow/checkpoint.py +311 -0
  104. alma/workflow/context.py +228 -0
  105. alma/workflow/outcomes.py +189 -0
  106. alma/workflow/reducers.py +393 -0
  107. {alma_memory-0.5.1.dist-info → alma_memory-0.7.0.dist-info}/METADATA +210 -72
  108. alma_memory-0.7.0.dist-info/RECORD +112 -0
  109. alma_memory-0.5.1.dist-info/RECORD +0 -93
  110. {alma_memory-0.5.1.dist-info → alma_memory-0.7.0.dist-info}/WHEEL +0 -0
  111. {alma_memory-0.5.1.dist-info → alma_memory-0.7.0.dist-info}/top_level.txt +0 -0
alma/session/types.py CHANGED
@@ -1,288 +1,288 @@
1
- """
2
- Session Management Types.
3
-
4
- Data models for session continuity and handoffs.
5
- """
6
-
7
- import uuid
8
- from dataclasses import dataclass, field
9
- from datetime import datetime, timezone
10
- from typing import Any, Dict, List, Literal, Optional
11
-
12
- SessionOutcome = Literal["success", "failure", "interrupted", "unknown"]
13
-
14
-
15
- @dataclass
16
- class SessionHandoff:
17
- """
18
- Compressed context for session continuity.
19
-
20
- Captures the essential state at session end so the next session
21
- can quickly resume without full context reconstruction.
22
- """
23
-
24
- id: str
25
- project_id: str
26
- agent: str
27
- session_id: str
28
-
29
- # Where we left off
30
- last_action: str
31
- last_outcome: SessionOutcome
32
- current_goal: str
33
-
34
- # Quick context (not full history)
35
- key_decisions: List[str] = field(default_factory=list) # Max 10 most important
36
- active_files: List[str] = field(default_factory=list) # Files being worked on
37
- blockers: List[str] = field(default_factory=list) # Current blockers
38
- next_steps: List[str] = field(default_factory=list) # Planned next actions
39
-
40
- # Test/validation state
41
- test_status: Dict[str, bool] = field(default_factory=dict) # test_name -> passing
42
-
43
- # Confidence signals
44
- confidence_level: float = 0.5 # 0-1, how well is this going
45
- risk_flags: List[str] = field(default_factory=list) # Concerns noted
46
-
47
- # Timing
48
- session_start: datetime = field(default_factory=lambda: datetime.now(timezone.utc))
49
- session_end: Optional[datetime] = None
50
- duration_ms: int = 0
51
-
52
- # For semantic retrieval
53
- embedding: Optional[List[float]] = None
54
-
55
- # Timestamps
56
- created_at: datetime = field(default_factory=lambda: datetime.now(timezone.utc))
57
-
58
- # Extensible metadata
59
- metadata: Dict[str, Any] = field(default_factory=dict)
60
-
61
- @classmethod
62
- def create(
63
- cls,
64
- project_id: str,
65
- agent: str,
66
- session_id: str,
67
- last_action: str,
68
- current_goal: str,
69
- last_outcome: SessionOutcome = "unknown",
70
- session_start: Optional[datetime] = None,
71
- **kwargs,
72
- ) -> "SessionHandoff":
73
- """Factory method to create a new session handoff."""
74
- return cls(
75
- id=str(uuid.uuid4()),
76
- project_id=project_id,
77
- agent=agent,
78
- session_id=session_id,
79
- last_action=last_action,
80
- last_outcome=last_outcome,
81
- current_goal=current_goal,
82
- session_start=session_start or datetime.now(timezone.utc),
83
- **kwargs,
84
- )
85
-
86
- def finalize(
87
- self,
88
- last_action: str,
89
- last_outcome: SessionOutcome,
90
- next_steps: Optional[List[str]] = None,
91
- ) -> None:
92
- """Finalize the handoff at session end."""
93
- self.last_action = last_action
94
- self.last_outcome = last_outcome
95
- self.session_end = datetime.now(timezone.utc)
96
- if self.session_start:
97
- self.duration_ms = int(
98
- (self.session_end - self.session_start).total_seconds() * 1000
99
- )
100
- if next_steps:
101
- self.next_steps = next_steps[:10] # Cap at 10
102
-
103
- def add_decision(self, decision: str) -> None:
104
- """Record a key decision (max 10)."""
105
- if len(self.key_decisions) >= 10:
106
- self.key_decisions.pop(0) # Remove oldest
107
- self.key_decisions.append(decision)
108
-
109
- def add_blocker(self, blocker: str) -> None:
110
- """Record a blocker."""
111
- if blocker not in self.blockers:
112
- self.blockers.append(blocker)
113
-
114
- def remove_blocker(self, blocker: str) -> None:
115
- """Remove a resolved blocker."""
116
- if blocker in self.blockers:
117
- self.blockers.remove(blocker)
118
-
119
- def set_test_status(self, test_name: str, passing: bool) -> None:
120
- """Update test status."""
121
- self.test_status[test_name] = passing
122
-
123
- def format_quick_reload(self) -> str:
124
- """
125
- Format handoff as a quick reload string.
126
-
127
- Returns a compact string that can be parsed by the next session
128
- for rapid context restoration.
129
- """
130
- lines = [
131
- f"## Session Handoff: {self.session_id}",
132
- f"Agent: {self.agent}",
133
- f"Goal: {self.current_goal}",
134
- f"Last Action: {self.last_action} ({self.last_outcome})",
135
- f"Confidence: {int(self.confidence_level * 100)}%",
136
- ]
137
-
138
- if self.next_steps:
139
- lines.append("\n### Next Steps:")
140
- for step in self.next_steps[:5]:
141
- lines.append(f"- {step}")
142
-
143
- if self.blockers:
144
- lines.append("\n### Blockers:")
145
- for blocker in self.blockers:
146
- lines.append(f"- {blocker}")
147
-
148
- if self.key_decisions:
149
- lines.append("\n### Key Decisions:")
150
- for decision in self.key_decisions[-5:]: # Last 5 decisions
151
- lines.append(f"- {decision}")
152
-
153
- if self.active_files:
154
- lines.append("\n### Active Files:")
155
- for f in self.active_files[:5]:
156
- lines.append(f"- {f}")
157
-
158
- if self.risk_flags:
159
- lines.append("\n### Risks:")
160
- for risk in self.risk_flags:
161
- lines.append(f"- {risk}")
162
-
163
- # Test summary
164
- if self.test_status:
165
- passing = sum(1 for v in self.test_status.values() if v)
166
- total = len(self.test_status)
167
- lines.append(f"\n### Tests: {passing}/{total} passing")
168
-
169
- return "\n".join(lines)
170
-
171
-
172
- @dataclass
173
- class SessionContext:
174
- """
175
- Full context for starting/resuming a session.
176
-
177
- Provides everything an agent needs to orient itself at session start.
178
- """
179
-
180
- project_id: str
181
- agent: str
182
- session_id: str
183
-
184
- # Handoff from previous session
185
- previous_handoff: Optional[SessionHandoff] = None
186
-
187
- # Current progress state (from ProgressSummary)
188
- progress: Optional[Any] = None # Will be ProgressSummary when integrated
189
-
190
- # Recent outcomes (from ALMA)
191
- recent_outcomes: List[Any] = field(default_factory=list)
192
-
193
- # Relevant heuristics (from ALMA)
194
- relevant_heuristics: List[Any] = field(default_factory=list)
195
-
196
- # Environment orientation
197
- codebase_state: Optional[Dict[str, Any]] = None # git status, recent commits
198
- environment_state: Optional[Dict[str, Any]] = None # running services, etc.
199
-
200
- # Suggested focus
201
- suggested_focus: Optional[Any] = None # WorkItem when integrated
202
-
203
- # Rules of engagement
204
- rules_of_engagement: List[str] = field(default_factory=list)
205
-
206
- # Timestamps
207
- created_at: datetime = field(default_factory=lambda: datetime.now(timezone.utc))
208
-
209
- @classmethod
210
- def create(
211
- cls,
212
- project_id: str,
213
- agent: str,
214
- session_id: Optional[str] = None,
215
- **kwargs,
216
- ) -> "SessionContext":
217
- """Factory method to create new session context."""
218
- return cls(
219
- project_id=project_id,
220
- agent=agent,
221
- session_id=session_id or str(uuid.uuid4()),
222
- **kwargs,
223
- )
224
-
225
- def format_orientation(self) -> str:
226
- """
227
- Format context as an orientation briefing.
228
-
229
- Returns a structured string for quick agent orientation.
230
- """
231
- lines = [
232
- "# Session Orientation",
233
- f"Project: {self.project_id}",
234
- f"Agent: {self.agent}",
235
- f"Session: {self.session_id}",
236
- ]
237
-
238
- # Previous session summary
239
- if self.previous_handoff:
240
- lines.append("\n## Previous Session")
241
- lines.append(f"Outcome: {self.previous_handoff.last_outcome}")
242
- lines.append(f"Last Action: {self.previous_handoff.last_action}")
243
- lines.append(f"Goal: {self.previous_handoff.current_goal}")
244
- if self.previous_handoff.next_steps:
245
- lines.append("Next Steps from Last Session:")
246
- for step in self.previous_handoff.next_steps[:3]:
247
- lines.append(f" - {step}")
248
-
249
- # Current progress
250
- if self.progress:
251
- lines.append("\n## Progress")
252
- # Assuming progress has a format_summary method
253
- if hasattr(self.progress, "format_summary"):
254
- lines.append(self.progress.format_summary())
255
- else:
256
- lines.append(str(self.progress))
257
-
258
- # Blockers from previous session
259
- if self.previous_handoff and self.previous_handoff.blockers:
260
- lines.append("\n## Outstanding Blockers")
261
- for blocker in self.previous_handoff.blockers:
262
- lines.append(f"- {blocker}")
263
-
264
- # Suggested focus
265
- if self.suggested_focus:
266
- lines.append("\n## Suggested Focus")
267
- if hasattr(self.suggested_focus, "title"):
268
- lines.append(f"Task: {self.suggested_focus.title}")
269
- else:
270
- lines.append(str(self.suggested_focus))
271
-
272
- # Environment state
273
- if self.codebase_state:
274
- lines.append("\n## Codebase State")
275
- if "branch" in self.codebase_state:
276
- lines.append(f"Branch: {self.codebase_state['branch']}")
277
- if "uncommitted" in self.codebase_state:
278
- lines.append(
279
- f"Uncommitted Changes: {self.codebase_state['uncommitted']}"
280
- )
281
-
282
- # Rules
283
- if self.rules_of_engagement:
284
- lines.append("\n## Rules of Engagement")
285
- for rule in self.rules_of_engagement:
286
- lines.append(f"- {rule}")
287
-
288
- return "\n".join(lines)
1
+ """
2
+ Session Management Types.
3
+
4
+ Data models for session continuity and handoffs.
5
+ """
6
+
7
+ import uuid
8
+ from dataclasses import dataclass, field
9
+ from datetime import datetime, timezone
10
+ from typing import Any, Dict, List, Literal, Optional
11
+
12
+ SessionOutcome = Literal["success", "failure", "interrupted", "unknown"]
13
+
14
+
15
+ @dataclass
16
+ class SessionHandoff:
17
+ """
18
+ Compressed context for session continuity.
19
+
20
+ Captures the essential state at session end so the next session
21
+ can quickly resume without full context reconstruction.
22
+ """
23
+
24
+ id: str
25
+ project_id: str
26
+ agent: str
27
+ session_id: str
28
+
29
+ # Where we left off
30
+ last_action: str
31
+ last_outcome: SessionOutcome
32
+ current_goal: str
33
+
34
+ # Quick context (not full history)
35
+ key_decisions: List[str] = field(default_factory=list) # Max 10 most important
36
+ active_files: List[str] = field(default_factory=list) # Files being worked on
37
+ blockers: List[str] = field(default_factory=list) # Current blockers
38
+ next_steps: List[str] = field(default_factory=list) # Planned next actions
39
+
40
+ # Test/validation state
41
+ test_status: Dict[str, bool] = field(default_factory=dict) # test_name -> passing
42
+
43
+ # Confidence signals
44
+ confidence_level: float = 0.5 # 0-1, how well is this going
45
+ risk_flags: List[str] = field(default_factory=list) # Concerns noted
46
+
47
+ # Timing
48
+ session_start: datetime = field(default_factory=lambda: datetime.now(timezone.utc))
49
+ session_end: Optional[datetime] = None
50
+ duration_ms: int = 0
51
+
52
+ # For semantic retrieval
53
+ embedding: Optional[List[float]] = None
54
+
55
+ # Timestamps
56
+ created_at: datetime = field(default_factory=lambda: datetime.now(timezone.utc))
57
+
58
+ # Extensible metadata
59
+ metadata: Dict[str, Any] = field(default_factory=dict)
60
+
61
+ @classmethod
62
+ def create(
63
+ cls,
64
+ project_id: str,
65
+ agent: str,
66
+ session_id: str,
67
+ last_action: str,
68
+ current_goal: str,
69
+ last_outcome: SessionOutcome = "unknown",
70
+ session_start: Optional[datetime] = None,
71
+ **kwargs,
72
+ ) -> "SessionHandoff":
73
+ """Factory method to create a new session handoff."""
74
+ return cls(
75
+ id=str(uuid.uuid4()),
76
+ project_id=project_id,
77
+ agent=agent,
78
+ session_id=session_id,
79
+ last_action=last_action,
80
+ last_outcome=last_outcome,
81
+ current_goal=current_goal,
82
+ session_start=session_start or datetime.now(timezone.utc),
83
+ **kwargs,
84
+ )
85
+
86
+ def finalize(
87
+ self,
88
+ last_action: str,
89
+ last_outcome: SessionOutcome,
90
+ next_steps: Optional[List[str]] = None,
91
+ ) -> None:
92
+ """Finalize the handoff at session end."""
93
+ self.last_action = last_action
94
+ self.last_outcome = last_outcome
95
+ self.session_end = datetime.now(timezone.utc)
96
+ if self.session_start:
97
+ self.duration_ms = int(
98
+ (self.session_end - self.session_start).total_seconds() * 1000
99
+ )
100
+ if next_steps:
101
+ self.next_steps = next_steps[:10] # Cap at 10
102
+
103
+ def add_decision(self, decision: str) -> None:
104
+ """Record a key decision (max 10)."""
105
+ if len(self.key_decisions) >= 10:
106
+ self.key_decisions.pop(0) # Remove oldest
107
+ self.key_decisions.append(decision)
108
+
109
+ def add_blocker(self, blocker: str) -> None:
110
+ """Record a blocker."""
111
+ if blocker not in self.blockers:
112
+ self.blockers.append(blocker)
113
+
114
+ def remove_blocker(self, blocker: str) -> None:
115
+ """Remove a resolved blocker."""
116
+ if blocker in self.blockers:
117
+ self.blockers.remove(blocker)
118
+
119
+ def set_test_status(self, test_name: str, passing: bool) -> None:
120
+ """Update test status."""
121
+ self.test_status[test_name] = passing
122
+
123
+ def format_quick_reload(self) -> str:
124
+ """
125
+ Format handoff as a quick reload string.
126
+
127
+ Returns a compact string that can be parsed by the next session
128
+ for rapid context restoration.
129
+ """
130
+ lines = [
131
+ f"## Session Handoff: {self.session_id}",
132
+ f"Agent: {self.agent}",
133
+ f"Goal: {self.current_goal}",
134
+ f"Last Action: {self.last_action} ({self.last_outcome})",
135
+ f"Confidence: {int(self.confidence_level * 100)}%",
136
+ ]
137
+
138
+ if self.next_steps:
139
+ lines.append("\n### Next Steps:")
140
+ for step in self.next_steps[:5]:
141
+ lines.append(f"- {step}")
142
+
143
+ if self.blockers:
144
+ lines.append("\n### Blockers:")
145
+ for blocker in self.blockers:
146
+ lines.append(f"- {blocker}")
147
+
148
+ if self.key_decisions:
149
+ lines.append("\n### Key Decisions:")
150
+ for decision in self.key_decisions[-5:]: # Last 5 decisions
151
+ lines.append(f"- {decision}")
152
+
153
+ if self.active_files:
154
+ lines.append("\n### Active Files:")
155
+ for f in self.active_files[:5]:
156
+ lines.append(f"- {f}")
157
+
158
+ if self.risk_flags:
159
+ lines.append("\n### Risks:")
160
+ for risk in self.risk_flags:
161
+ lines.append(f"- {risk}")
162
+
163
+ # Test summary
164
+ if self.test_status:
165
+ passing = sum(1 for v in self.test_status.values() if v)
166
+ total = len(self.test_status)
167
+ lines.append(f"\n### Tests: {passing}/{total} passing")
168
+
169
+ return "\n".join(lines)
170
+
171
+
172
+ @dataclass
173
+ class SessionContext:
174
+ """
175
+ Full context for starting/resuming a session.
176
+
177
+ Provides everything an agent needs to orient itself at session start.
178
+ """
179
+
180
+ project_id: str
181
+ agent: str
182
+ session_id: str
183
+
184
+ # Handoff from previous session
185
+ previous_handoff: Optional[SessionHandoff] = None
186
+
187
+ # Current progress state (from ProgressSummary)
188
+ progress: Optional[Any] = None # Will be ProgressSummary when integrated
189
+
190
+ # Recent outcomes (from ALMA)
191
+ recent_outcomes: List[Any] = field(default_factory=list)
192
+
193
+ # Relevant heuristics (from ALMA)
194
+ relevant_heuristics: List[Any] = field(default_factory=list)
195
+
196
+ # Environment orientation
197
+ codebase_state: Optional[Dict[str, Any]] = None # git status, recent commits
198
+ environment_state: Optional[Dict[str, Any]] = None # running services, etc.
199
+
200
+ # Suggested focus
201
+ suggested_focus: Optional[Any] = None # WorkItem when integrated
202
+
203
+ # Rules of engagement
204
+ rules_of_engagement: List[str] = field(default_factory=list)
205
+
206
+ # Timestamps
207
+ created_at: datetime = field(default_factory=lambda: datetime.now(timezone.utc))
208
+
209
+ @classmethod
210
+ def create(
211
+ cls,
212
+ project_id: str,
213
+ agent: str,
214
+ session_id: Optional[str] = None,
215
+ **kwargs,
216
+ ) -> "SessionContext":
217
+ """Factory method to create new session context."""
218
+ return cls(
219
+ project_id=project_id,
220
+ agent=agent,
221
+ session_id=session_id or str(uuid.uuid4()),
222
+ **kwargs,
223
+ )
224
+
225
+ def format_orientation(self) -> str:
226
+ """
227
+ Format context as an orientation briefing.
228
+
229
+ Returns a structured string for quick agent orientation.
230
+ """
231
+ lines = [
232
+ "# Session Orientation",
233
+ f"Project: {self.project_id}",
234
+ f"Agent: {self.agent}",
235
+ f"Session: {self.session_id}",
236
+ ]
237
+
238
+ # Previous session summary
239
+ if self.previous_handoff:
240
+ lines.append("\n## Previous Session")
241
+ lines.append(f"Outcome: {self.previous_handoff.last_outcome}")
242
+ lines.append(f"Last Action: {self.previous_handoff.last_action}")
243
+ lines.append(f"Goal: {self.previous_handoff.current_goal}")
244
+ if self.previous_handoff.next_steps:
245
+ lines.append("Next Steps from Last Session:")
246
+ for step in self.previous_handoff.next_steps[:3]:
247
+ lines.append(f" - {step}")
248
+
249
+ # Current progress
250
+ if self.progress:
251
+ lines.append("\n## Progress")
252
+ # Assuming progress has a format_summary method
253
+ if hasattr(self.progress, "format_summary"):
254
+ lines.append(self.progress.format_summary())
255
+ else:
256
+ lines.append(str(self.progress))
257
+
258
+ # Blockers from previous session
259
+ if self.previous_handoff and self.previous_handoff.blockers:
260
+ lines.append("\n## Outstanding Blockers")
261
+ for blocker in self.previous_handoff.blockers:
262
+ lines.append(f"- {blocker}")
263
+
264
+ # Suggested focus
265
+ if self.suggested_focus:
266
+ lines.append("\n## Suggested Focus")
267
+ if hasattr(self.suggested_focus, "title"):
268
+ lines.append(f"Task: {self.suggested_focus.title}")
269
+ else:
270
+ lines.append(str(self.suggested_focus))
271
+
272
+ # Environment state
273
+ if self.codebase_state:
274
+ lines.append("\n## Codebase State")
275
+ if "branch" in self.codebase_state:
276
+ lines.append(f"Branch: {self.codebase_state['branch']}")
277
+ if "uncommitted" in self.codebase_state:
278
+ lines.append(
279
+ f"Uncommitted Changes: {self.codebase_state['uncommitted']}"
280
+ )
281
+
282
+ # Rules
283
+ if self.rules_of_engagement:
284
+ lines.append("\n## Rules of Engagement")
285
+ for rule in self.rules_of_engagement:
286
+ lines.append(f"- {rule}")
287
+
288
+ return "\n".join(lines)