htmlgraph 0.26.25__py3-none-any.whl → 0.27.1__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 (175) hide show
  1. htmlgraph/__init__.py +23 -1
  2. htmlgraph/__init__.pyi +123 -0
  3. htmlgraph/agent_registry.py +2 -1
  4. htmlgraph/analytics/cli.py +3 -3
  5. htmlgraph/analytics/cost_analyzer.py +5 -1
  6. htmlgraph/analytics/cost_monitor.py +664 -0
  7. htmlgraph/analytics/cross_session.py +13 -9
  8. htmlgraph/analytics/dependency.py +10 -6
  9. htmlgraph/analytics/strategic/__init__.py +80 -0
  10. htmlgraph/analytics/strategic/cost_optimizer.py +611 -0
  11. htmlgraph/analytics/strategic/pattern_detector.py +876 -0
  12. htmlgraph/analytics/strategic/preference_manager.py +709 -0
  13. htmlgraph/analytics/strategic/suggestion_engine.py +747 -0
  14. htmlgraph/analytics/work_type.py +15 -11
  15. htmlgraph/analytics_index.py +2 -1
  16. htmlgraph/api/cost_alerts_websocket.py +416 -0
  17. htmlgraph/api/main.py +167 -62
  18. htmlgraph/api/websocket.py +538 -0
  19. htmlgraph/attribute_index.py +2 -1
  20. htmlgraph/builders/base.py +2 -1
  21. htmlgraph/builders/bug.py +2 -1
  22. htmlgraph/builders/chore.py +2 -1
  23. htmlgraph/builders/epic.py +2 -1
  24. htmlgraph/builders/feature.py +2 -1
  25. htmlgraph/builders/insight.py +2 -1
  26. htmlgraph/builders/metric.py +2 -1
  27. htmlgraph/builders/pattern.py +2 -1
  28. htmlgraph/builders/phase.py +2 -1
  29. htmlgraph/builders/spike.py +2 -1
  30. htmlgraph/builders/track.py +2 -1
  31. htmlgraph/cli/analytics.py +2 -1
  32. htmlgraph/cli/base.py +2 -1
  33. htmlgraph/cli/core.py +2 -1
  34. htmlgraph/cli/main.py +2 -1
  35. htmlgraph/cli/models.py +2 -1
  36. htmlgraph/cli/templates/cost_dashboard.py +2 -1
  37. htmlgraph/cli/work/__init__.py +2 -1
  38. htmlgraph/cli/work/browse.py +2 -1
  39. htmlgraph/cli/work/features.py +2 -1
  40. htmlgraph/cli/work/orchestration.py +2 -1
  41. htmlgraph/cli/work/report.py +2 -1
  42. htmlgraph/cli/work/sessions.py +2 -1
  43. htmlgraph/cli/work/snapshot.py +2 -1
  44. htmlgraph/cli/work/tracks.py +2 -1
  45. htmlgraph/collections/base.py +10 -5
  46. htmlgraph/collections/bug.py +2 -1
  47. htmlgraph/collections/chore.py +2 -1
  48. htmlgraph/collections/epic.py +2 -1
  49. htmlgraph/collections/feature.py +2 -1
  50. htmlgraph/collections/insight.py +2 -1
  51. htmlgraph/collections/metric.py +2 -1
  52. htmlgraph/collections/pattern.py +2 -1
  53. htmlgraph/collections/phase.py +2 -1
  54. htmlgraph/collections/session.py +12 -7
  55. htmlgraph/collections/spike.py +6 -1
  56. htmlgraph/collections/task_delegation.py +7 -2
  57. htmlgraph/collections/todo.py +2 -1
  58. htmlgraph/collections/traces.py +15 -10
  59. htmlgraph/config/cost_models.json +56 -0
  60. htmlgraph/context_analytics.py +2 -1
  61. htmlgraph/db/schema.py +67 -6
  62. htmlgraph/dependency_models.py +2 -1
  63. htmlgraph/edge_index.py +2 -1
  64. htmlgraph/event_log.py +83 -64
  65. htmlgraph/event_migration.py +2 -1
  66. htmlgraph/file_watcher.py +12 -8
  67. htmlgraph/find_api.py +2 -1
  68. htmlgraph/git_events.py +6 -2
  69. htmlgraph/hooks/cigs_pretool_enforcer.py +5 -1
  70. htmlgraph/hooks/drift_handler.py +3 -3
  71. htmlgraph/hooks/event_tracker.py +40 -61
  72. htmlgraph/hooks/installer.py +5 -1
  73. htmlgraph/hooks/orchestrator.py +4 -0
  74. htmlgraph/hooks/orchestrator_reflector.py +4 -0
  75. htmlgraph/hooks/post_tool_use_failure.py +7 -3
  76. htmlgraph/hooks/posttooluse.py +4 -0
  77. htmlgraph/hooks/prompt_analyzer.py +5 -5
  78. htmlgraph/hooks/session_handler.py +2 -1
  79. htmlgraph/hooks/session_summary.py +6 -2
  80. htmlgraph/hooks/validator.py +8 -4
  81. htmlgraph/ids.py +2 -1
  82. htmlgraph/learning.py +2 -1
  83. htmlgraph/mcp_server.py +2 -1
  84. htmlgraph/operations/analytics.py +2 -1
  85. htmlgraph/operations/bootstrap.py +2 -1
  86. htmlgraph/operations/events.py +2 -1
  87. htmlgraph/operations/fastapi_server.py +2 -1
  88. htmlgraph/operations/hooks.py +2 -1
  89. htmlgraph/operations/initialization.py +2 -1
  90. htmlgraph/operations/server.py +2 -1
  91. htmlgraph/orchestration/claude_launcher.py +23 -20
  92. htmlgraph/orchestration/command_builder.py +2 -1
  93. htmlgraph/orchestration/headless_spawner.py +6 -2
  94. htmlgraph/orchestration/model_selection.py +7 -3
  95. htmlgraph/orchestration/plugin_manager.py +24 -19
  96. htmlgraph/orchestration/spawners/claude.py +5 -2
  97. htmlgraph/orchestration/spawners/codex.py +12 -19
  98. htmlgraph/orchestration/spawners/copilot.py +13 -18
  99. htmlgraph/orchestration/spawners/gemini.py +12 -19
  100. htmlgraph/orchestration/subprocess_runner.py +6 -3
  101. htmlgraph/orchestration/task_coordination.py +16 -8
  102. htmlgraph/orchestrator.py +2 -1
  103. htmlgraph/parallel.py +2 -1
  104. htmlgraph/query_builder.py +2 -1
  105. htmlgraph/reflection.py +2 -1
  106. htmlgraph/refs.py +2 -1
  107. htmlgraph/repo_hash.py +2 -1
  108. htmlgraph/repositories/__init__.py +292 -0
  109. htmlgraph/repositories/analytics_repository.py +455 -0
  110. htmlgraph/repositories/analytics_repository_standard.py +628 -0
  111. htmlgraph/repositories/feature_repository.py +581 -0
  112. htmlgraph/repositories/feature_repository_htmlfile.py +668 -0
  113. htmlgraph/repositories/feature_repository_memory.py +607 -0
  114. htmlgraph/repositories/feature_repository_sqlite.py +858 -0
  115. htmlgraph/repositories/filter_service.py +620 -0
  116. htmlgraph/repositories/filter_service_standard.py +445 -0
  117. htmlgraph/repositories/shared_cache.py +621 -0
  118. htmlgraph/repositories/shared_cache_memory.py +395 -0
  119. htmlgraph/repositories/track_repository.py +552 -0
  120. htmlgraph/repositories/track_repository_htmlfile.py +619 -0
  121. htmlgraph/repositories/track_repository_memory.py +508 -0
  122. htmlgraph/repositories/track_repository_sqlite.py +711 -0
  123. htmlgraph/sdk/__init__.py +398 -0
  124. htmlgraph/sdk/__init__.pyi +14 -0
  125. htmlgraph/sdk/analytics/__init__.py +19 -0
  126. htmlgraph/sdk/analytics/engine.py +155 -0
  127. htmlgraph/sdk/analytics/helpers.py +178 -0
  128. htmlgraph/sdk/analytics/registry.py +109 -0
  129. htmlgraph/sdk/base.py +484 -0
  130. htmlgraph/sdk/constants.py +216 -0
  131. htmlgraph/sdk/core.pyi +308 -0
  132. htmlgraph/sdk/discovery.py +120 -0
  133. htmlgraph/sdk/help/__init__.py +12 -0
  134. htmlgraph/sdk/help/mixin.py +699 -0
  135. htmlgraph/sdk/mixins/__init__.py +15 -0
  136. htmlgraph/sdk/mixins/attribution.py +113 -0
  137. htmlgraph/sdk/mixins/mixin.py +410 -0
  138. htmlgraph/sdk/operations/__init__.py +12 -0
  139. htmlgraph/sdk/operations/mixin.py +427 -0
  140. htmlgraph/sdk/orchestration/__init__.py +17 -0
  141. htmlgraph/sdk/orchestration/coordinator.py +203 -0
  142. htmlgraph/sdk/orchestration/spawner.py +204 -0
  143. htmlgraph/sdk/planning/__init__.py +19 -0
  144. htmlgraph/sdk/planning/bottlenecks.py +93 -0
  145. htmlgraph/sdk/planning/mixin.py +211 -0
  146. htmlgraph/sdk/planning/parallel.py +186 -0
  147. htmlgraph/sdk/planning/queue.py +210 -0
  148. htmlgraph/sdk/planning/recommendations.py +87 -0
  149. htmlgraph/sdk/planning/smart_planning.py +319 -0
  150. htmlgraph/sdk/session/__init__.py +19 -0
  151. htmlgraph/sdk/session/continuity.py +57 -0
  152. htmlgraph/sdk/session/handoff.py +110 -0
  153. htmlgraph/sdk/session/info.py +309 -0
  154. htmlgraph/sdk/session/manager.py +103 -0
  155. htmlgraph/sdk/strategic/__init__.py +26 -0
  156. htmlgraph/sdk/strategic/mixin.py +563 -0
  157. htmlgraph/server.py +21 -17
  158. htmlgraph/session_warning.py +2 -1
  159. htmlgraph/sessions/handoff.py +4 -3
  160. htmlgraph/system_prompts.py +2 -1
  161. htmlgraph/track_builder.py +2 -1
  162. htmlgraph/transcript.py +2 -1
  163. htmlgraph/watch.py +2 -1
  164. htmlgraph/work_type_utils.py +2 -1
  165. {htmlgraph-0.26.25.dist-info → htmlgraph-0.27.1.dist-info}/METADATA +1 -1
  166. htmlgraph-0.27.1.dist-info/RECORD +332 -0
  167. htmlgraph/sdk.py +0 -3500
  168. htmlgraph-0.26.25.dist-info/RECORD +0 -274
  169. {htmlgraph-0.26.25.data → htmlgraph-0.27.1.data}/data/htmlgraph/dashboard.html +0 -0
  170. {htmlgraph-0.26.25.data → htmlgraph-0.27.1.data}/data/htmlgraph/styles.css +0 -0
  171. {htmlgraph-0.26.25.data → htmlgraph-0.27.1.data}/data/htmlgraph/templates/AGENTS.md.template +0 -0
  172. {htmlgraph-0.26.25.data → htmlgraph-0.27.1.data}/data/htmlgraph/templates/CLAUDE.md.template +0 -0
  173. {htmlgraph-0.26.25.data → htmlgraph-0.27.1.data}/data/htmlgraph/templates/GEMINI.md.template +0 -0
  174. {htmlgraph-0.26.25.dist-info → htmlgraph-0.27.1.dist-info}/WHEEL +0 -0
  175. {htmlgraph-0.26.25.dist-info → htmlgraph-0.27.1.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,319 @@
1
+ """Smart planning workflow integration."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import TYPE_CHECKING, Any
6
+
7
+ if TYPE_CHECKING:
8
+ from htmlgraph.models import Node
9
+
10
+
11
+ def start_planning_spike(
12
+ sdk: Any,
13
+ title: str,
14
+ context: str = "",
15
+ timebox_hours: float = 4.0,
16
+ auto_start: bool = True,
17
+ ) -> Node:
18
+ """
19
+ Create a planning spike to research and design before implementation.
20
+
21
+ This is for timeboxed investigation before creating a full track.
22
+
23
+ Args:
24
+ sdk: SDK instance
25
+ title: Spike title (e.g., "Plan User Authentication System")
26
+ context: Background information
27
+ timebox_hours: Time limit for spike (default: 4 hours)
28
+ auto_start: Automatically start the spike (default: True)
29
+
30
+ Returns:
31
+ Created spike Node
32
+
33
+ Example:
34
+ >>> sdk = SDK(agent="claude")
35
+ >>> spike = sdk.start_planning_spike(
36
+ ... "Plan Real-time Notifications",
37
+ ... context="Users need live updates. Research options.",
38
+ ... timebox_hours=3.0
39
+ ... )
40
+ """
41
+ from htmlgraph.ids import generate_id
42
+ from htmlgraph.models import Spike, SpikeType, Step
43
+
44
+ # Create spike directly (SpikeBuilder doesn't exist yet)
45
+ spike_id = generate_id(node_type="spike", title=title)
46
+ spike = Spike(
47
+ id=spike_id,
48
+ title=title,
49
+ type="spike",
50
+ status="in-progress" if auto_start and sdk._agent_id else "todo",
51
+ spike_type=SpikeType.ARCHITECTURAL,
52
+ timebox_hours=int(timebox_hours),
53
+ agent_assigned=sdk._agent_id if auto_start and sdk._agent_id else None,
54
+ steps=[
55
+ Step(description="Research existing solutions and patterns"),
56
+ Step(description="Define requirements and constraints"),
57
+ Step(description="Design high-level architecture"),
58
+ Step(description="Identify dependencies and risks"),
59
+ Step(description="Create implementation plan"),
60
+ ],
61
+ content=f"<p>{context}</p>" if context else "",
62
+ edges={},
63
+ properties={},
64
+ )
65
+
66
+ sdk._graph.add(spike)
67
+ return spike
68
+
69
+
70
+ def create_track_from_plan(
71
+ sdk: Any,
72
+ title: str,
73
+ description: str,
74
+ spike_id: str | None = None,
75
+ priority: str = "high",
76
+ requirements: list[str | tuple[str, str]] | None = None,
77
+ phases: list[tuple[str, list[str]]] | None = None,
78
+ ) -> dict[str, Any]:
79
+ """
80
+ Create a track with spec and plan from planning results.
81
+
82
+ Args:
83
+ sdk: SDK instance
84
+ title: Track title
85
+ description: Track description
86
+ spike_id: Optional spike ID that led to this track
87
+ priority: Track priority (default: "high")
88
+ requirements: List of requirements (strings or (req, priority) tuples)
89
+ phases: List of (phase_name, tasks) tuples for the plan
90
+
91
+ Returns:
92
+ Dict with track, spec, and plan details
93
+
94
+ Example:
95
+ >>> sdk = SDK(agent="claude")
96
+ >>> track_info = sdk.create_track_from_plan(
97
+ ... title="User Authentication System",
98
+ ... description="OAuth 2.0 with JWT tokens",
99
+ ... requirements=[
100
+ ... ("OAuth 2.0 integration", "must-have"),
101
+ ... ("JWT token management", "must-have"),
102
+ ... "Password reset flow"
103
+ ... ],
104
+ ... phases=[
105
+ ... ("Phase 1: OAuth", ["Setup providers (2h)", "Callback (2h)"]),
106
+ ... ("Phase 2: JWT", ["Token signing (2h)", "Refresh (1.5h)"])
107
+ ... ]
108
+ ... )
109
+ """
110
+
111
+ builder = (
112
+ sdk.tracks.builder().title(title).description(description).priority(priority)
113
+ )
114
+
115
+ # Add reference to planning spike if provided
116
+ if spike_id:
117
+ # Access internal data for track builder
118
+ data: dict[str, Any] = builder._data # type: ignore[attr-defined]
119
+ data["properties"]["planning_spike"] = spike_id
120
+
121
+ # Add spec if requirements provided
122
+ if requirements:
123
+ # Convert simple strings to (requirement, "must-have") tuples
124
+ req_list = []
125
+ for req in requirements:
126
+ if isinstance(req, str):
127
+ req_list.append((req, "must-have"))
128
+ else:
129
+ req_list.append(req)
130
+
131
+ builder.with_spec(
132
+ overview=description,
133
+ context=f"Track created from planning spike: {spike_id}"
134
+ if spike_id
135
+ else "",
136
+ requirements=req_list,
137
+ acceptance_criteria=[],
138
+ )
139
+
140
+ # Add plan if phases provided
141
+ if phases:
142
+ builder.with_plan_phases(phases)
143
+
144
+ track = builder.create()
145
+
146
+ return {
147
+ "track_id": track.id,
148
+ "title": track.title,
149
+ "has_spec": bool(requirements),
150
+ "has_plan": bool(phases),
151
+ "spike_id": spike_id,
152
+ "priority": priority,
153
+ }
154
+
155
+
156
+ def smart_plan(
157
+ sdk: Any,
158
+ description: str,
159
+ create_spike: bool = True,
160
+ timebox_hours: float = 4.0,
161
+ research_completed: bool = False,
162
+ research_findings: dict[str, Any] | None = None,
163
+ ) -> dict[str, Any]:
164
+ """
165
+ Smart planning workflow: analyzes project context and creates spike or track.
166
+
167
+ This is the main entry point for planning new work. It:
168
+ 1. Checks current project state
169
+ 2. Provides context from strategic analytics
170
+ 3. Creates a planning spike or track as appropriate
171
+
172
+ **IMPORTANT: Research Phase Required**
173
+ For complex features, you should complete research BEFORE planning:
174
+ 1. Use /htmlgraph:research or WebSearch to gather best practices
175
+ 2. Document findings (libraries, patterns, anti-patterns)
176
+ 3. Pass research_completed=True and research_findings to this method
177
+ 4. This ensures planning is informed by industry best practices
178
+
179
+ Research-first workflow:
180
+ 1. /htmlgraph:research "{topic}" → Gather external knowledge
181
+ 2. sdk.smart_plan(..., research_completed=True) → Plan with context
182
+ 3. Complete spike steps → Design solution
183
+ 4. Create track from plan → Structure implementation
184
+
185
+ Args:
186
+ sdk: SDK instance
187
+ description: What you want to plan (e.g., "User authentication system")
188
+ create_spike: Create a spike for research (default: True)
189
+ timebox_hours: If creating spike, time limit (default: 4 hours)
190
+ research_completed: Whether research was performed (default: False)
191
+ research_findings: Structured research findings (optional)
192
+
193
+ Returns:
194
+ Dict with planning context and created spike/track info
195
+
196
+ Example:
197
+ >>> sdk = SDK(agent="claude")
198
+ >>> # WITH research (recommended for complex work)
199
+ >>> research = {
200
+ ... "topic": "OAuth 2.0 best practices",
201
+ ... "sources_count": 5,
202
+ ... "recommended_library": "authlib",
203
+ ... "key_insights": ["Use PKCE", "Implement token rotation"]
204
+ ... }
205
+ >>> plan = sdk.smart_plan(
206
+ ... "User authentication system",
207
+ ... create_spike=True,
208
+ ... research_completed=True,
209
+ ... research_findings=research
210
+ ... )
211
+ >>> logger.info(f"Created: {plan['spike_id']}")
212
+ >>> logger.info(f"Research informed: {plan['research_informed']}")
213
+ """
214
+ # Get project context from strategic analytics
215
+ from htmlgraph.sdk.planning.bottlenecks import assess_risks, find_bottlenecks
216
+ from htmlgraph.sdk.planning.parallel import get_parallel_work
217
+
218
+ bottlenecks = find_bottlenecks(sdk, top_n=3)
219
+ risks = assess_risks(sdk)
220
+ parallel = get_parallel_work(sdk, max_agents=5)
221
+
222
+ context = {
223
+ "bottlenecks_count": len(bottlenecks),
224
+ "high_risk_count": risks["high_risk_count"],
225
+ "parallel_capacity": parallel["max_parallelism"],
226
+ "description": description,
227
+ }
228
+
229
+ # Build context string with research info
230
+ context_str = f"Project context:\n- {len(bottlenecks)} bottlenecks\n- {risks['high_risk_count']} high-risk items\n- {parallel['max_parallelism']} parallel capacity"
231
+
232
+ if research_completed and research_findings:
233
+ context_str += f"\n\nResearch completed:\n- Topic: {research_findings.get('topic', description)}"
234
+ if "sources_count" in research_findings:
235
+ context_str += f"\n- Sources: {research_findings['sources_count']}"
236
+ if "recommended_library" in research_findings:
237
+ context_str += (
238
+ f"\n- Recommended: {research_findings['recommended_library']}"
239
+ )
240
+
241
+ # Validation: warn if complex work planned without research
242
+ is_complex = any(
243
+ [
244
+ "auth" in description.lower(),
245
+ "security" in description.lower(),
246
+ "real-time" in description.lower(),
247
+ "websocket" in description.lower(),
248
+ "oauth" in description.lower(),
249
+ "performance" in description.lower(),
250
+ "integration" in description.lower(),
251
+ ]
252
+ )
253
+
254
+ warnings = []
255
+ if is_complex and not research_completed:
256
+ warnings.append(
257
+ "⚠️ Complex feature detected without research. "
258
+ "Consider using /htmlgraph:research first to gather best practices."
259
+ )
260
+
261
+ if create_spike:
262
+ spike = start_planning_spike(
263
+ sdk,
264
+ title=f"Plan: {description}",
265
+ context=context_str,
266
+ timebox_hours=timebox_hours,
267
+ )
268
+
269
+ # Store research metadata in spike properties if provided
270
+ if research_completed and research_findings:
271
+ spike.properties["research_completed"] = True
272
+ spike.properties["research_findings"] = research_findings
273
+ sdk._graph.update(spike)
274
+
275
+ result = {
276
+ "type": "spike",
277
+ "spike_id": spike.id,
278
+ "title": spike.title,
279
+ "status": spike.status,
280
+ "timebox_hours": timebox_hours,
281
+ "project_context": context,
282
+ "research_informed": research_completed,
283
+ "next_steps": [
284
+ "Research and design the solution"
285
+ if not research_completed
286
+ else "Design solution using research findings",
287
+ "Complete spike steps",
288
+ "Use SDK.create_track_from_plan() to create track",
289
+ ],
290
+ }
291
+
292
+ if warnings:
293
+ result["warnings"] = warnings
294
+
295
+ return result
296
+ else:
297
+ # Direct track creation (for when you already know what to do)
298
+ track_info = create_track_from_plan(
299
+ sdk,
300
+ title=description,
301
+ description=f"Planned with context: {context}",
302
+ )
303
+
304
+ result = {
305
+ "type": "track",
306
+ **track_info,
307
+ "project_context": context,
308
+ "research_informed": research_completed,
309
+ "next_steps": [
310
+ "Create features from track plan",
311
+ "Link features to track",
312
+ "Start implementation",
313
+ ],
314
+ }
315
+
316
+ if warnings:
317
+ result["warnings"] = warnings
318
+
319
+ return result
@@ -0,0 +1,19 @@
1
+ """
2
+ Session management submodule for HtmlGraph SDK.
3
+
4
+ Provides session lifecycle operations, handoff context, and continuity.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ from htmlgraph.sdk.session.continuity import SessionContinuityMixin
10
+ from htmlgraph.sdk.session.handoff import SessionHandoffMixin
11
+ from htmlgraph.sdk.session.info import SessionInfoMixin
12
+ from htmlgraph.sdk.session.manager import SessionManagerMixin
13
+
14
+ __all__ = [
15
+ "SessionManagerMixin",
16
+ "SessionHandoffMixin",
17
+ "SessionContinuityMixin",
18
+ "SessionInfoMixin",
19
+ ]
@@ -0,0 +1,57 @@
1
+ """
2
+ Session continuity and resume operations for SDK.
3
+ """
4
+
5
+ from __future__ import annotations
6
+
7
+ from typing import TYPE_CHECKING, Any
8
+
9
+ if TYPE_CHECKING:
10
+ from htmlgraph.session_manager import SessionManager
11
+
12
+
13
+ class SessionContinuityMixin:
14
+ """
15
+ Provides session continuity operations for resuming work.
16
+
17
+ Attributes accessed by mixins:
18
+ session_manager: SessionManager instance
19
+ _agent_id: Agent identifier
20
+ """
21
+
22
+ session_manager: SessionManager
23
+
24
+ def continue_from_last(
25
+ self,
26
+ agent: str | None = None,
27
+ auto_create_session: bool = True,
28
+ ) -> tuple[Any, Any]:
29
+ """
30
+ Continue work from the last completed session.
31
+
32
+ Loads context from previous session including handoff notes,
33
+ recommended files, blockers, and recent commits.
34
+
35
+ Args:
36
+ agent: Filter by agent (None = current SDK agent)
37
+ auto_create_session: Create new session if True
38
+
39
+ Returns:
40
+ Tuple of (new_session, resume_info) or (None, None)
41
+
42
+ Example:
43
+ >>> sdk = SDK(agent="claude")
44
+ >>> session, resume = sdk.continue_from_last()
45
+ >>> if resume:
46
+ ... logger.info("%s", resume.summary)
47
+ ... logger.info("%s", resume.next_focus)
48
+ ... for file in resume.recommended_files:
49
+ ... logger.info(f" - {file}")
50
+ """
51
+ if not agent:
52
+ agent = self._agent_id # type: ignore[attr-defined]
53
+
54
+ return self.session_manager.continue_from_last(
55
+ agent=agent,
56
+ auto_create_session=auto_create_session,
57
+ )
@@ -0,0 +1,110 @@
1
+ """
2
+ Session handoff context management for SDK.
3
+ """
4
+
5
+ from __future__ import annotations
6
+
7
+ from typing import TYPE_CHECKING, Any
8
+
9
+ if TYPE_CHECKING:
10
+ from htmlgraph.session_manager import SessionManager
11
+
12
+
13
+ class SessionHandoffMixin:
14
+ """
15
+ Provides session handoff operations for cross-session continuity.
16
+
17
+ Attributes accessed by mixins:
18
+ session_manager: SessionManager instance
19
+ _agent_id: Agent identifier
20
+ """
21
+
22
+ session_manager: SessionManager
23
+
24
+ def set_session_handoff(
25
+ self,
26
+ handoff_notes: str | None = None,
27
+ recommended_next: str | None = None,
28
+ blockers: list[str] | None = None,
29
+ session_id: str | None = None,
30
+ ) -> Any:
31
+ """
32
+ Set handoff context on a session.
33
+
34
+ Args:
35
+ handoff_notes: Notes for next session/agent
36
+ recommended_next: Suggested next steps
37
+ blockers: List of blockers
38
+ session_id: Specific session ID (defaults to active session)
39
+
40
+ Returns:
41
+ Updated Session or None if not found
42
+ """
43
+ if not session_id:
44
+ if self._agent_id: # type: ignore[attr-defined]
45
+ active = self.session_manager.get_active_session_for_agent(
46
+ self._agent_id # type: ignore[attr-defined]
47
+ )
48
+ else:
49
+ active = self.session_manager.get_active_session()
50
+ if not active:
51
+ return None
52
+ session_id = active.id
53
+
54
+ return self.session_manager.set_session_handoff(
55
+ session_id=session_id,
56
+ handoff_notes=handoff_notes,
57
+ recommended_next=recommended_next,
58
+ blockers=blockers,
59
+ )
60
+
61
+ def end_session_with_handoff(
62
+ self,
63
+ session_id: str | None = None,
64
+ summary: str | None = None,
65
+ next_focus: str | None = None,
66
+ blockers: list[str] | None = None,
67
+ keep_context: list[str] | None = None,
68
+ auto_recommend_context: bool = True,
69
+ ) -> Any:
70
+ """
71
+ End session with handoff information for next session.
72
+
73
+ Args:
74
+ session_id: Session to end (None = active session)
75
+ summary: What was accomplished
76
+ next_focus: What should be done next
77
+ blockers: List of blockers
78
+ keep_context: List of files to keep context for
79
+ auto_recommend_context: Auto-recommend files from git
80
+
81
+ Returns:
82
+ Updated Session or None
83
+
84
+ Example:
85
+ >>> sdk.end_session_with_handoff(
86
+ ... summary="Completed OAuth integration",
87
+ ... next_focus="Implement JWT token refresh",
88
+ ... blockers=["Waiting for security review"],
89
+ ... keep_context=["src/auth/oauth.py"]
90
+ ... )
91
+ """
92
+ if not session_id:
93
+ if self._agent_id: # type: ignore[attr-defined]
94
+ active = self.session_manager.get_active_session_for_agent(
95
+ self._agent_id # type: ignore[attr-defined]
96
+ )
97
+ else:
98
+ active = self.session_manager.get_active_session()
99
+ if not active:
100
+ return None
101
+ session_id = active.id
102
+
103
+ return self.session_manager.end_session_with_handoff(
104
+ session_id=session_id,
105
+ summary=summary,
106
+ next_focus=next_focus,
107
+ blockers=blockers,
108
+ keep_context=keep_context,
109
+ auto_recommend_context=auto_recommend_context,
110
+ )