bazaar-compute-node 0.1.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.
Files changed (62) hide show
  1. bazaar_compute_node/__init__.py +3 -0
  2. bazaar_compute_node/app/__init__.py +1 -0
  3. bazaar_compute_node/app/application.py +398 -0
  4. bazaar_compute_node/app/attachments.py +154 -0
  5. bazaar_compute_node/app/command.py +342 -0
  6. bazaar_compute_node/app/config.py +121 -0
  7. bazaar_compute_node/app/registry.py +120 -0
  8. bazaar_compute_node/app/transport.py +264 -0
  9. bazaar_compute_node/app/windows_pipe.py +463 -0
  10. bazaar_compute_node/app/wrapper.py +63 -0
  11. bazaar_compute_node/bcc.py +524 -0
  12. bazaar_compute_node/cli.py +382 -0
  13. bazaar_compute_node/contrib/__init__.py +1 -0
  14. bazaar_compute_node/contrib/codex_app_server/__init__.py +63 -0
  15. bazaar_compute_node/contrib/codex_app_server/approval.py +168 -0
  16. bazaar_compute_node/contrib/codex_app_server/client.py +408 -0
  17. bazaar_compute_node/contrib/codex_app_server/events.py +431 -0
  18. bazaar_compute_node/contrib/codex_app_server/plugin.py +15 -0
  19. bazaar_compute_node/contrib/codex_app_server/process.py +583 -0
  20. bazaar_compute_node/contrib/codex_app_server/protocol.py +103 -0
  21. bazaar_compute_node/contrib/codex_app_server/runtime.py +513 -0
  22. bazaar_compute_node/contrib/logging/__init__.py +5 -0
  23. bazaar_compute_node/contrib/logging/audit.py +61 -0
  24. bazaar_compute_node/contrib/logging/plugin.py +11 -0
  25. bazaar_compute_node/contrib/sqlite/__init__.py +14 -0
  26. bazaar_compute_node/contrib/sqlite/codec.py +768 -0
  27. bazaar_compute_node/contrib/sqlite/database.py +282 -0
  28. bazaar_compute_node/contrib/sqlite/migrations.py +646 -0
  29. bazaar_compute_node/contrib/sqlite/plugin.py +11 -0
  30. bazaar_compute_node/contrib/sqlite/repository.py +1059 -0
  31. bazaar_compute_node/contrib/wecom/__init__.py +1 -0
  32. bazaar_compute_node/contrib/wecom/channel.py +960 -0
  33. bazaar_compute_node/contrib/wecom/markdown.py +146 -0
  34. bazaar_compute_node/contrib/wecom/plugin.py +29 -0
  35. bazaar_compute_node/core/__init__.py +5 -0
  36. bazaar_compute_node/core/approval.py +51 -0
  37. bazaar_compute_node/core/audit.py +101 -0
  38. bazaar_compute_node/core/channel.py +121 -0
  39. bazaar_compute_node/core/client.py +30 -0
  40. bazaar_compute_node/core/command.py +85 -0
  41. bazaar_compute_node/core/concurrency.py +29 -0
  42. bazaar_compute_node/core/correlation.py +48 -0
  43. bazaar_compute_node/core/instruction.py +224 -0
  44. bazaar_compute_node/core/lifecycle.py +48 -0
  45. bazaar_compute_node/core/models/__init__.py +63 -0
  46. bazaar_compute_node/core/models/entities.py +514 -0
  47. bazaar_compute_node/core/models/states.py +369 -0
  48. bazaar_compute_node/core/observability.py +47 -0
  49. bazaar_compute_node/core/orchestration/__init__.py +5 -0
  50. bazaar_compute_node/core/orchestration/command.py +614 -0
  51. bazaar_compute_node/core/orchestration/services.py +135 -0
  52. bazaar_compute_node/core/orchestration/session.py +891 -0
  53. bazaar_compute_node/core/orchestration/turn.py +451 -0
  54. bazaar_compute_node/core/outcomes.py +51 -0
  55. bazaar_compute_node/core/paths.py +19 -0
  56. bazaar_compute_node/core/runtime.py +118 -0
  57. bazaar_compute_node/core/storage.py +167 -0
  58. bazaar_compute_node-0.1.3.dist-info/METADATA +178 -0
  59. bazaar_compute_node-0.1.3.dist-info/RECORD +62 -0
  60. bazaar_compute_node-0.1.3.dist-info/WHEEL +4 -0
  61. bazaar_compute_node-0.1.3.dist-info/entry_points.txt +15 -0
  62. bazaar_compute_node-0.1.3.dist-info/licenses/LICENSE +613 -0
@@ -0,0 +1,369 @@
1
+ from __future__ import annotations
2
+
3
+ from collections.abc import Mapping
4
+ from dataclasses import dataclass
5
+ from enum import Enum, StrEnum
6
+
7
+
8
+ class AgentState(StrEnum):
9
+ """Provider-neutral lifecycle state for one bcn agent session."""
10
+
11
+ CREATED = "created"
12
+ STARTING = "starting"
13
+ IDLE = "idle"
14
+ WORKING = "working"
15
+ COMPACTION_STARTING = "compaction_starting"
16
+ COMPACTING = "compacting"
17
+ COMPACTION_COMPLETED = "compaction_completed"
18
+ STOPPING = "stopping"
19
+ FAILED = "failed"
20
+ UNKNOWN = "unknown"
21
+ RECONCILING = "reconciling"
22
+
23
+
24
+ class ChannelTargetKind(StrEnum):
25
+ DM = "dm"
26
+ GROUP = "group"
27
+
28
+
29
+ class AgentTickSource(StrEnum):
30
+ """Origin of an observation; the orchestrator remains the state writer."""
31
+
32
+ SESSION = "session"
33
+ CHANNEL = "channel"
34
+ RUNTIME = "runtime"
35
+ RECOVERY = "recovery"
36
+
37
+
38
+ class AgentSignal(StrEnum):
39
+ """Provider-neutral facts that the core reducer can apply to an agent."""
40
+
41
+ START_REQUESTED = "start_requested"
42
+ START_CONFIRMED = "start_confirmed"
43
+ TURN_STARTED = "turn_started"
44
+ TURN_COMPLETED = "turn_completed"
45
+ TURN_FAILED = "turn_failed"
46
+ TURN_CANCELLED = "turn_cancelled"
47
+ WORKING_OBSERVED = "working_observed"
48
+ COMPACTION_STARTED = "compaction_started"
49
+ COMPACTION_IN_PROGRESS = "compaction_in_progress"
50
+ COMPACTION_COMPLETED = "compaction_completed"
51
+ STOP_REQUESTED = "stop_requested"
52
+ FAILED = "failed"
53
+ UNKNOWN = "unknown"
54
+ RECONCILE_REQUESTED = "reconcile_requested"
55
+ RECONCILE_CONFIRMED = "reconcile_confirmed"
56
+
57
+
58
+ @dataclass(frozen=True, slots=True)
59
+ class AgentTick:
60
+ """An immutable, provider-neutral lifecycle observation."""
61
+
62
+ source: AgentTickSource
63
+ signal: AgentSignal
64
+ observed_at_ms: int
65
+ error_kind: str | None = None
66
+ error_message: str | None = None
67
+
68
+ def __post_init__(self) -> None:
69
+ if not isinstance(self.source, AgentTickSource):
70
+ raise TypeError("agent tick source is invalid")
71
+ if not isinstance(self.signal, AgentSignal):
72
+ raise TypeError("agent tick signal is invalid")
73
+ if not isinstance(self.observed_at_ms, int) or self.observed_at_ms < 0:
74
+ raise ValueError("observed_at_ms must be a non-negative integer")
75
+ if self.error_kind is not None and (
76
+ not isinstance(self.error_kind, str) or not self.error_kind
77
+ ):
78
+ raise ValueError("error_kind must be a non-empty string when provided")
79
+ if self.error_message is not None and not isinstance(self.error_message, str):
80
+ raise TypeError("error_message must be a string when provided")
81
+
82
+
83
+ AGENT_TICK_TRANSITIONS: Mapping[AgentState, Mapping[AgentSignal, AgentState]] = {
84
+ AgentState.CREATED: {
85
+ AgentSignal.START_REQUESTED: AgentState.STARTING,
86
+ AgentSignal.WORKING_OBSERVED: AgentState.WORKING,
87
+ AgentSignal.STOP_REQUESTED: AgentState.STOPPING,
88
+ AgentSignal.FAILED: AgentState.FAILED,
89
+ AgentSignal.UNKNOWN: AgentState.UNKNOWN,
90
+ },
91
+ AgentState.STARTING: {
92
+ AgentSignal.START_REQUESTED: AgentState.STARTING,
93
+ AgentSignal.START_CONFIRMED: AgentState.IDLE,
94
+ AgentSignal.WORKING_OBSERVED: AgentState.WORKING,
95
+ AgentSignal.STOP_REQUESTED: AgentState.STOPPING,
96
+ AgentSignal.FAILED: AgentState.FAILED,
97
+ AgentSignal.UNKNOWN: AgentState.UNKNOWN,
98
+ },
99
+ AgentState.IDLE: {
100
+ AgentSignal.START_CONFIRMED: AgentState.IDLE,
101
+ AgentSignal.WORKING_OBSERVED: AgentState.WORKING,
102
+ AgentSignal.TURN_STARTED: AgentState.WORKING,
103
+ AgentSignal.TURN_COMPLETED: AgentState.IDLE,
104
+ AgentSignal.TURN_FAILED: AgentState.IDLE,
105
+ AgentSignal.TURN_CANCELLED: AgentState.IDLE,
106
+ AgentSignal.COMPACTION_STARTED: AgentState.COMPACTION_STARTING,
107
+ AgentSignal.COMPACTION_IN_PROGRESS: AgentState.COMPACTING,
108
+ AgentSignal.COMPACTION_COMPLETED: AgentState.COMPACTION_COMPLETED,
109
+ AgentSignal.STOP_REQUESTED: AgentState.STOPPING,
110
+ AgentSignal.FAILED: AgentState.FAILED,
111
+ AgentSignal.UNKNOWN: AgentState.UNKNOWN,
112
+ AgentSignal.RECONCILE_CONFIRMED: AgentState.IDLE,
113
+ },
114
+ AgentState.WORKING: {
115
+ AgentSignal.WORKING_OBSERVED: AgentState.WORKING,
116
+ AgentSignal.TURN_STARTED: AgentState.WORKING,
117
+ AgentSignal.TURN_COMPLETED: AgentState.IDLE,
118
+ AgentSignal.TURN_FAILED: AgentState.IDLE,
119
+ AgentSignal.TURN_CANCELLED: AgentState.IDLE,
120
+ AgentSignal.COMPACTION_STARTED: AgentState.COMPACTION_STARTING,
121
+ AgentSignal.COMPACTION_IN_PROGRESS: AgentState.COMPACTING,
122
+ AgentSignal.COMPACTION_COMPLETED: AgentState.COMPACTION_COMPLETED,
123
+ AgentSignal.STOP_REQUESTED: AgentState.STOPPING,
124
+ AgentSignal.FAILED: AgentState.FAILED,
125
+ AgentSignal.UNKNOWN: AgentState.UNKNOWN,
126
+ },
127
+ AgentState.COMPACTION_STARTING: {
128
+ AgentSignal.WORKING_OBSERVED: AgentState.WORKING,
129
+ AgentSignal.COMPACTION_STARTED: AgentState.COMPACTION_STARTING,
130
+ AgentSignal.COMPACTION_IN_PROGRESS: AgentState.COMPACTING,
131
+ AgentSignal.COMPACTION_COMPLETED: AgentState.COMPACTION_COMPLETED,
132
+ AgentSignal.TURN_STARTED: AgentState.WORKING,
133
+ AgentSignal.TURN_COMPLETED: AgentState.IDLE,
134
+ AgentSignal.STOP_REQUESTED: AgentState.STOPPING,
135
+ AgentSignal.FAILED: AgentState.FAILED,
136
+ AgentSignal.UNKNOWN: AgentState.UNKNOWN,
137
+ },
138
+ AgentState.COMPACTING: {
139
+ AgentSignal.WORKING_OBSERVED: AgentState.WORKING,
140
+ AgentSignal.COMPACTION_STARTED: AgentState.COMPACTING,
141
+ AgentSignal.COMPACTION_IN_PROGRESS: AgentState.COMPACTING,
142
+ AgentSignal.COMPACTION_COMPLETED: AgentState.COMPACTION_COMPLETED,
143
+ AgentSignal.TURN_STARTED: AgentState.WORKING,
144
+ AgentSignal.TURN_COMPLETED: AgentState.IDLE,
145
+ AgentSignal.STOP_REQUESTED: AgentState.STOPPING,
146
+ AgentSignal.FAILED: AgentState.FAILED,
147
+ AgentSignal.UNKNOWN: AgentState.UNKNOWN,
148
+ },
149
+ AgentState.COMPACTION_COMPLETED: {
150
+ AgentSignal.WORKING_OBSERVED: AgentState.WORKING,
151
+ AgentSignal.COMPACTION_STARTED: AgentState.COMPACTION_STARTING,
152
+ AgentSignal.COMPACTION_IN_PROGRESS: AgentState.COMPACTING,
153
+ AgentSignal.COMPACTION_COMPLETED: AgentState.COMPACTION_COMPLETED,
154
+ AgentSignal.TURN_STARTED: AgentState.WORKING,
155
+ AgentSignal.TURN_COMPLETED: AgentState.IDLE,
156
+ AgentSignal.STOP_REQUESTED: AgentState.STOPPING,
157
+ AgentSignal.FAILED: AgentState.FAILED,
158
+ AgentSignal.UNKNOWN: AgentState.UNKNOWN,
159
+ },
160
+ AgentState.STOPPING: {
161
+ AgentSignal.STOP_REQUESTED: AgentState.STOPPING,
162
+ AgentSignal.FAILED: AgentState.FAILED,
163
+ AgentSignal.UNKNOWN: AgentState.UNKNOWN,
164
+ },
165
+ AgentState.FAILED: {
166
+ AgentSignal.START_REQUESTED: AgentState.STARTING,
167
+ AgentSignal.STOP_REQUESTED: AgentState.STOPPING,
168
+ AgentSignal.FAILED: AgentState.FAILED,
169
+ AgentSignal.TURN_FAILED: AgentState.FAILED,
170
+ AgentSignal.TURN_CANCELLED: AgentState.FAILED,
171
+ AgentSignal.UNKNOWN: AgentState.UNKNOWN,
172
+ },
173
+ AgentState.UNKNOWN: {
174
+ AgentSignal.RECONCILE_REQUESTED: AgentState.RECONCILING,
175
+ AgentSignal.STOP_REQUESTED: AgentState.STOPPING,
176
+ AgentSignal.UNKNOWN: AgentState.UNKNOWN,
177
+ },
178
+ AgentState.RECONCILING: {
179
+ AgentSignal.START_REQUESTED: AgentState.STARTING,
180
+ AgentSignal.START_CONFIRMED: AgentState.IDLE,
181
+ AgentSignal.WORKING_OBSERVED: AgentState.WORKING,
182
+ AgentSignal.RECONCILE_REQUESTED: AgentState.RECONCILING,
183
+ AgentSignal.RECONCILE_CONFIRMED: AgentState.IDLE,
184
+ AgentSignal.STOP_REQUESTED: AgentState.STOPPING,
185
+ AgentSignal.FAILED: AgentState.FAILED,
186
+ AgentSignal.UNKNOWN: AgentState.UNKNOWN,
187
+ },
188
+ }
189
+
190
+
191
+ AGENT_STATE_TRANSITIONS: Mapping[AgentState, frozenset[AgentState]] = {
192
+ current: frozenset(
193
+ target for target in signal_targets.values() if target is not current
194
+ )
195
+ for current, signal_targets in AGENT_TICK_TRANSITIONS.items()
196
+ }
197
+
198
+
199
+ def reduce_agent_tick(current: AgentState, tick: AgentTick) -> AgentState:
200
+ """Reduce one tick without mutating state or performing I/O."""
201
+
202
+ if not isinstance(current, AgentState):
203
+ raise TypeError("agent state is invalid")
204
+ if not isinstance(tick, AgentTick):
205
+ raise TypeError("agent tick is invalid")
206
+ target = AGENT_TICK_TRANSITIONS.get(current, {}).get(tick.signal)
207
+ if target is None:
208
+ raise StateTransitionError("agent", current, tick.signal)
209
+ ensure_transition("agent", current, target, AGENT_STATE_TRANSITIONS)
210
+ return target
211
+
212
+
213
+ class RuntimeTurnState(StrEnum):
214
+ STARTING = "starting"
215
+ RUNNING = "running"
216
+ COMPLETED = "completed"
217
+ FAILED = "failed"
218
+ CANCELLED = "cancelled"
219
+ UNKNOWN = "unknown"
220
+
221
+
222
+ class OutboundDeliveryState(StrEnum):
223
+ DRAFT = "draft"
224
+ PENDING = "pending"
225
+ QUEUED = "queued"
226
+ SENT = "sent"
227
+ PARTIAL = "partial"
228
+ FAILED = "failed"
229
+ UNKNOWN = "unknown"
230
+ REJECTED = "rejected"
231
+
232
+
233
+ class FreshCheckState(StrEnum):
234
+ REQUIRED = "required"
235
+ PASSED = "passed"
236
+ FAILED = "failed"
237
+
238
+
239
+ class RuntimeEventState(StrEnum):
240
+ STARTED = "started"
241
+ COMPLETED = "completed"
242
+ FAILED = "failed"
243
+ CANCELLED = "cancelled"
244
+ UNKNOWN = "unknown"
245
+
246
+
247
+ class StreamEventKind(StrEnum):
248
+ AGENT_MESSAGE_DELTA = "agent-message-delta"
249
+ PLAN_DELTA = "plan-delta"
250
+ REASONING_SUMMARY_DELTA = "reasoning-summary-delta"
251
+ REASONING_TEXT_DELTA = "reasoning-text-delta"
252
+ COMMAND_OUTPUT_DELTA = "command-output-delta"
253
+ COMMAND_INTERACTION = "command-interaction"
254
+ FILE_CHANGE_UPDATE = "file-change-update"
255
+ TOOL_PROGRESS = "tool-progress"
256
+ ITEM_PROGRESS = "item-progress"
257
+ TURN_PROGRESS = "turn-progress"
258
+
259
+
260
+ class ApprovalDecision(StrEnum):
261
+ APPROVED = "approved"
262
+ REJECTED = "rejected"
263
+
264
+
265
+ class StateTransitionError(ValueError):
266
+ def __init__(self, aggregate: str, current: Enum, target: Enum) -> None:
267
+ self.aggregate = aggregate
268
+ self.current = current
269
+ self.target = target
270
+ super().__init__(
271
+ f"Invalid {aggregate} state transition: {current.value} -> {target.value}"
272
+ )
273
+
274
+
275
+ def ensure_transition[StateT: Enum](
276
+ aggregate: str,
277
+ current: StateT,
278
+ target: StateT,
279
+ transitions: Mapping[StateT, frozenset[StateT]],
280
+ ) -> None:
281
+ if current is target:
282
+ return
283
+ if target not in transitions.get(current, frozenset()):
284
+ raise StateTransitionError(aggregate, current, target)
285
+
286
+
287
+ RUNTIME_TURN_TRANSITIONS: Mapping[RuntimeTurnState, frozenset[RuntimeTurnState]] = {
288
+ RuntimeTurnState.STARTING: frozenset(
289
+ {
290
+ RuntimeTurnState.RUNNING,
291
+ RuntimeTurnState.FAILED,
292
+ RuntimeTurnState.CANCELLED,
293
+ RuntimeTurnState.UNKNOWN,
294
+ }
295
+ ),
296
+ RuntimeTurnState.RUNNING: frozenset(
297
+ {
298
+ RuntimeTurnState.COMPLETED,
299
+ RuntimeTurnState.FAILED,
300
+ RuntimeTurnState.CANCELLED,
301
+ RuntimeTurnState.UNKNOWN,
302
+ }
303
+ ),
304
+ RuntimeTurnState.COMPLETED: frozenset(),
305
+ RuntimeTurnState.FAILED: frozenset(),
306
+ RuntimeTurnState.CANCELLED: frozenset(),
307
+ RuntimeTurnState.UNKNOWN: frozenset(),
308
+ }
309
+
310
+ OUTBOUND_DELIVERY_TRANSITIONS: Mapping[
311
+ OutboundDeliveryState, frozenset[OutboundDeliveryState]
312
+ ] = {
313
+ OutboundDeliveryState.DRAFT: frozenset(
314
+ {OutboundDeliveryState.PENDING, OutboundDeliveryState.REJECTED}
315
+ ),
316
+ OutboundDeliveryState.PENDING: frozenset(
317
+ {
318
+ OutboundDeliveryState.QUEUED,
319
+ OutboundDeliveryState.SENT,
320
+ OutboundDeliveryState.PARTIAL,
321
+ OutboundDeliveryState.FAILED,
322
+ OutboundDeliveryState.UNKNOWN,
323
+ }
324
+ ),
325
+ OutboundDeliveryState.QUEUED: frozenset(
326
+ {
327
+ OutboundDeliveryState.SENT,
328
+ OutboundDeliveryState.PARTIAL,
329
+ OutboundDeliveryState.FAILED,
330
+ OutboundDeliveryState.UNKNOWN,
331
+ }
332
+ ),
333
+ OutboundDeliveryState.SENT: frozenset(),
334
+ OutboundDeliveryState.PARTIAL: frozenset(),
335
+ OutboundDeliveryState.FAILED: frozenset(),
336
+ OutboundDeliveryState.UNKNOWN: frozenset(
337
+ {
338
+ OutboundDeliveryState.SENT,
339
+ OutboundDeliveryState.PARTIAL,
340
+ OutboundDeliveryState.FAILED,
341
+ }
342
+ ),
343
+ OutboundDeliveryState.REJECTED: frozenset(),
344
+ }
345
+
346
+ FRESH_CHECK_TRANSITIONS: Mapping[FreshCheckState, frozenset[FreshCheckState]] = {
347
+ FreshCheckState.REQUIRED: frozenset(
348
+ {FreshCheckState.PASSED, FreshCheckState.FAILED}
349
+ ),
350
+ FreshCheckState.PASSED: frozenset(),
351
+ FreshCheckState.FAILED: frozenset(),
352
+ }
353
+
354
+ RUNTIME_EVENT_TRANSITIONS: Mapping[RuntimeEventState, frozenset[RuntimeEventState]] = {
355
+ RuntimeEventState.STARTED: frozenset(
356
+ {
357
+ RuntimeEventState.COMPLETED,
358
+ RuntimeEventState.FAILED,
359
+ RuntimeEventState.CANCELLED,
360
+ RuntimeEventState.UNKNOWN,
361
+ }
362
+ ),
363
+ RuntimeEventState.COMPLETED: frozenset(),
364
+ RuntimeEventState.FAILED: frozenset(),
365
+ RuntimeEventState.CANCELLED: frozenset(),
366
+ RuntimeEventState.UNKNOWN: frozenset(
367
+ {RuntimeEventState.COMPLETED, RuntimeEventState.FAILED}
368
+ ),
369
+ }
@@ -0,0 +1,47 @@
1
+ from __future__ import annotations
2
+
3
+ from collections.abc import Mapping
4
+ from dataclasses import dataclass, field
5
+ from enum import StrEnum
6
+ from typing import TYPE_CHECKING, Protocol
7
+
8
+ if TYPE_CHECKING:
9
+ from .audit import AuditEvent
10
+
11
+
12
+ class LogLevel(StrEnum):
13
+ DEBUG = "debug"
14
+ INFO = "info"
15
+ WARNING = "warning"
16
+ ERROR = "error"
17
+
18
+
19
+ @dataclass(frozen=True, slots=True)
20
+ class LogRecord:
21
+ """Structured runtime log record without raw provider payloads."""
22
+
23
+ level: LogLevel
24
+ event_name: str
25
+ message: str
26
+ fields: Mapping[str, object] = field(default_factory=dict)
27
+
28
+
29
+ class ILogger(Protocol):
30
+ """Synchronous, non-blocking stderr logging boundary."""
31
+
32
+ def emit(self, record: LogRecord, *, error: BaseException | None = None) -> None:
33
+ """Emit a redacted record without blocking the asyncio event loop."""
34
+ ...
35
+
36
+
37
+ class IAudit(Protocol):
38
+ """Durable append-only operational event boundary."""
39
+
40
+ @property
41
+ def name(self) -> str:
42
+ """Return the stable entry-point identity of this adapter."""
43
+ ...
44
+
45
+ async def append(self, event: AuditEvent, *, timeout: float) -> None:
46
+ """Persist one event; cancellation must propagate to the caller."""
47
+ ...
@@ -0,0 +1,5 @@
1
+ """Core session orchestration independent of provider implementations."""
2
+
3
+ from .session import SessionOrchestrator
4
+
5
+ __all__ = ["SessionOrchestrator"]