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,646 @@
1
+ from __future__ import annotations
2
+
3
+ from collections.abc import Callable
4
+ from dataclasses import dataclass
5
+ from hashlib import sha256
6
+ from time import monotonic_ns, time_ns
7
+ from typing import TYPE_CHECKING
8
+
9
+ if TYPE_CHECKING:
10
+ from .repository import SqliteTransaction
11
+
12
+
13
+ def _current_time_ms() -> int:
14
+ return time_ns() // 1_000_000
15
+
16
+
17
+ @dataclass(frozen=True, slots=True)
18
+ class Migration:
19
+ version: int
20
+ name: str
21
+ statements: tuple[str, ...]
22
+
23
+ @property
24
+ def checksum(self) -> str:
25
+ content = "\n".join(self.statements).encode("utf-8")
26
+ return sha256(content).hexdigest()
27
+
28
+
29
+ class MigrationError(RuntimeError):
30
+ """The database cannot be safely brought to the application schema."""
31
+
32
+
33
+ class MigrationChecksumError(MigrationError):
34
+ """A migration ledger entry no longer matches the application migration."""
35
+
36
+
37
+ SCHEMA_MIGRATION = Migration(
38
+ version=1,
39
+ name="initial_node_schema",
40
+ statements=(
41
+ """
42
+ -- Immutable migration ledger and checksum verification record.
43
+ CREATE TABLE schema_migrations (
44
+ -- Monotonic migration version used for application-level ledger checks.
45
+ version INTEGER PRIMARY KEY,
46
+ -- Human-readable migration name.
47
+ migration_name TEXT,
48
+ -- Migration content checksum.
49
+ checksum TEXT,
50
+ -- Migration application time.
51
+ applied_at_ms INTEGER,
52
+ -- Migration execution duration.
53
+ duration_ms INTEGER
54
+ )
55
+ """,
56
+ """
57
+ -- Singleton node identity, workspace binding, and cached schema version.
58
+ CREATE TABLE node_state (
59
+ -- Fixed application-managed row key for the singleton state record.
60
+ singleton_key INTEGER PRIMARY KEY,
61
+ -- Stable identifier of this node installation.
62
+ node_id TEXT,
63
+ -- Cached version of the migration ledger.
64
+ schema_version INTEGER,
65
+ -- UUIDv7-backed identifier of the shared workspace used by all runtime sessions.
66
+ workspace_id TEXT,
67
+ -- Creation time of the node state.
68
+ created_at_ms INTEGER,
69
+ -- Last update time of node metadata or schema cache.
70
+ updated_at_ms INTEGER,
71
+ -- Non-sensitive node metadata encoded as JSON.
72
+ metadata_json TEXT
73
+ )
74
+ """,
75
+ """
76
+ -- Provider thread identity and channel-level following state.
77
+ CREATE TABLE channel_sessions (
78
+ -- Stable local identifier for the normalized channel session.
79
+ id TEXT PRIMARY KEY,
80
+ -- Selected channel adapter name.
81
+ channel TEXT,
82
+ -- Provider-native routable thread identity used for lookup.
83
+ provider_thread_id TEXT,
84
+ -- Normalized target category used by the command layer.
85
+ target_kind TEXT,
86
+ -- Application-managed following flag.
87
+ following INTEGER,
88
+ -- Non-sensitive provider identity references encoded as JSON.
89
+ provider_identity_ref_json TEXT,
90
+ -- Creation time of the channel session.
91
+ created_at_ms INTEGER,
92
+ -- Last update time of channel identity or lifecycle state.
93
+ updated_at_ms INTEGER,
94
+ -- Last normalized inbound time observed for this session.
95
+ last_inbound_at_ms INTEGER,
96
+ -- Last outbound attempt time observed for this session.
97
+ last_outbound_at_ms INTEGER
98
+ )
99
+ """,
100
+ """
101
+ -- Stable bcn session bound to one channel session and the shared workspace.
102
+ CREATE TABLE bcn_sessions (
103
+ -- Stable local identifier exposed to the runtime command wrapper.
104
+ id TEXT PRIMARY KEY,
105
+ -- Application-managed association to a channel session.
106
+ channel_session_id TEXT,
107
+ -- UUIDv7-backed identifier of the shared workspace used by this session.
108
+ workspace_id TEXT,
109
+ -- Creation time of the bcn session.
110
+ created_at_ms INTEGER,
111
+ -- Last update time of durable session metadata.
112
+ updated_at_ms INTEGER,
113
+ -- Last message or runtime activity time.
114
+ last_activity_at_ms INTEGER,
115
+ -- Non-sensitive session metadata encoded as JSON.
116
+ metadata_json TEXT
117
+ )
118
+ """,
119
+ """
120
+ -- One durable agent runtime/thread binding.
121
+ CREATE TABLE runtime_sessions (
122
+ -- Stable local identifier for one runtime binding.
123
+ id TEXT PRIMARY KEY,
124
+ -- Application-managed association to a bcn session.
125
+ bcn_session_id TEXT,
126
+ -- Application-managed channel session association for correlation.
127
+ channel_session_id TEXT,
128
+ -- Selected agent runtime adapter name.
129
+ runtime TEXT,
130
+ -- Runtime adapter or protocol version used for this process.
131
+ runtime_version TEXT,
132
+ -- Provider-native runtime thread identifier when available.
133
+ provider_thread_id TEXT,
134
+ -- Creation time of the runtime session record.
135
+ created_at_ms INTEGER,
136
+ -- Last update time of durable runtime metadata.
137
+ updated_at_ms INTEGER,
138
+ -- Non-sensitive runtime metadata encoded as JSON.
139
+ metadata_json TEXT
140
+ )
141
+ """,
142
+ """
143
+ -- Durable runtime turn state used for completion, interruption, and reconciliation.
144
+ CREATE TABLE runtime_turns (
145
+ -- Stable local identifier for one runtime turn.
146
+ turn_id TEXT PRIMARY KEY,
147
+ -- Application-managed association to the runtime session.
148
+ session_id TEXT,
149
+ -- Provider-native turn identifier when available.
150
+ provider_turn_id TEXT,
151
+ -- Client message identifier that caused this turn.
152
+ client_user_message_id TEXT,
153
+ -- Application-managed turn lifecycle state.
154
+ state TEXT,
155
+ -- Turn start time.
156
+ started_at_ms INTEGER,
157
+ -- Turn completion time when a terminal result is known.
158
+ completed_at_ms INTEGER,
159
+ -- Latest normalized runtime event name.
160
+ last_event_name TEXT,
161
+ -- Stable application error category for the turn.
162
+ error_kind TEXT,
163
+ -- Redacted summary of the turn failure.
164
+ error_message TEXT,
165
+ -- Non-sensitive turn metadata encoded as JSON.
166
+ metadata_json TEXT
167
+ )
168
+ """,
169
+ """
170
+ -- Append-only normalized inbound message log with the node-local delivery sequence.
171
+ CREATE TABLE inbound_messages (
172
+ -- Stable local UUIDv7 message identifier used as the physical row identity.
173
+ message_id TEXT PRIMARY KEY,
174
+ -- Node-local monotonic sequence used for cursor and snapshot boundaries.
175
+ seq INTEGER,
176
+ -- Application-managed association to a bcn session.
177
+ session_id TEXT,
178
+ -- Application-managed association to a channel session.
179
+ channel_session_id TEXT,
180
+ -- Channel adapter name that normalized the message.
181
+ channel TEXT,
182
+ -- Provider-native routable thread identity mapped to the channel session.
183
+ provider_thread_id TEXT,
184
+ -- Provider-native message identifier used for application-level deduplication.
185
+ provider_message_id TEXT,
186
+ -- Provider timestamp, if supplied.
187
+ provider_time_ms INTEGER,
188
+ -- Local receipt time.
189
+ received_at_ms INTEGER,
190
+ -- Provider-neutral sender identity shown to the runtime.
191
+ sender TEXT,
192
+ -- Normalized sender or event type.
193
+ message_type TEXT,
194
+ -- Canonical target used by reply commands.
195
+ canonical_target TEXT,
196
+ -- Provider-neutral direct-message or group classification.
197
+ target_kind TEXT,
198
+ -- Provider-native identifier of the message being replied to.
199
+ reply_to_provider_message_id TEXT,
200
+ -- Normalized message body.
201
+ body TEXT,
202
+ -- Whether the provider reports an explicit mention of the agent.
203
+ mentions_agent INTEGER,
204
+ -- Persisted application decision to expose this message as unread.
205
+ notifies_runtime INTEGER,
206
+ -- Controlled reference to retained provider payload data.
207
+ provider_payload_ref TEXT,
208
+ -- Non-sensitive normalized metadata encoded as JSON.
209
+ metadata_json TEXT
210
+ )
211
+ """,
212
+ """
213
+ -- Terminal local descriptors for provider-neutral inbound attachments.
214
+ CREATE TABLE inbound_attachments (
215
+ attachment_id TEXT PRIMARY KEY,
216
+ message_id TEXT,
217
+ ordinal INTEGER,
218
+ name TEXT,
219
+ kind TEXT,
220
+ state TEXT,
221
+ media_type TEXT,
222
+ relative_path TEXT,
223
+ size_bytes INTEGER,
224
+ error TEXT
225
+ )
226
+ """,
227
+ """
228
+ -- Outbound command attempts, fresh-check evidence, provider receipt, and delivery state.
229
+ CREATE TABLE outbound_messages (
230
+ -- Stable local UUIDv7 identifier for one outbound command attempt.
231
+ outbound_message_id TEXT PRIMARY KEY,
232
+ -- Stable identifier of the originating command invocation.
233
+ command_id TEXT,
234
+ -- Application-managed association to a bcn session.
235
+ session_id TEXT,
236
+ -- Application-managed association to a channel session.
237
+ channel_session_id TEXT,
238
+ -- Canonical target supplied to the send command.
239
+ target TEXT,
240
+ -- Local inbound message identity for an optional reply intent.
241
+ reply_to_message_id TEXT,
242
+ -- Outbound message body captured for retry and audit.
243
+ body TEXT,
244
+ -- Application-managed delivery lifecycle state.
245
+ state TEXT,
246
+ -- Application-managed fresh-check result.
247
+ fresh_check_state TEXT,
248
+ -- Inbound snapshot boundary used by the command.
249
+ snapshot_seq INTEGER,
250
+ -- Current inbound boundary observed during fresh-check.
251
+ current_inbound_seq INTEGER,
252
+ -- Provider-native message identifier after provider acceptance.
253
+ provider_message_id TEXT,
254
+ -- Controlled reference to the provider delivery receipt.
255
+ provider_receipt_ref TEXT,
256
+ -- Creation time of the outbound attempt.
257
+ created_at_ms INTEGER,
258
+ -- Time at which the provider call was attempted.
259
+ provider_attempted_at_ms INTEGER,
260
+ -- Completion time of the provider call.
261
+ completed_at_ms INTEGER,
262
+ -- Time at which a refused draft was persisted.
263
+ draft_saved_at_ms INTEGER,
264
+ -- Stable application error category for the attempt.
265
+ error_kind TEXT,
266
+ -- Redacted summary of the outbound failure.
267
+ error_message TEXT,
268
+ -- Human- and machine-actionable next step.
269
+ next_action TEXT,
270
+ -- Non-sensitive outbound metadata encoded as JSON.
271
+ metadata_json TEXT
272
+ )
273
+ """,
274
+ """
275
+ -- Per-session delivery cursor and the latest inbox snapshot used by fresh-check.
276
+ CREATE TABLE consumer_cursors (
277
+ -- Stable bcn session identifier used as the cursor record identity.
278
+ session_id TEXT PRIMARY KEY,
279
+ -- Highest inbound sequence already delivered by check.
280
+ delivered_through_seq INTEGER,
281
+ -- Latest inbound sequence observed by check or read.
282
+ inbox_snapshot_seq INTEGER,
283
+ -- Operation that produced the latest snapshot.
284
+ inbox_snapshot_source TEXT,
285
+ -- Time at which the latest snapshot was recorded.
286
+ inbox_snapshot_at_ms INTEGER,
287
+ -- Last check operation time.
288
+ last_check_at_ms INTEGER,
289
+ -- Last read operation time.
290
+ last_read_at_ms INTEGER,
291
+ -- Last cursor or snapshot update time.
292
+ updated_at_ms INTEGER
293
+ )
294
+ """,
295
+ """
296
+ -- Append-only operational and audit events with cross-component correlation fields.
297
+ CREATE TABLE runtime_events (
298
+ -- Node-local monotonic sequence for the event log.
299
+ event_seq INTEGER PRIMARY KEY,
300
+ -- Stable event identifier for external correlation.
301
+ event_id TEXT,
302
+ -- Event creation time.
303
+ created_at_ms INTEGER,
304
+ -- Normalized log severity.
305
+ level TEXT,
306
+ -- Stable event name.
307
+ event_name TEXT,
308
+ -- Application-managed event state.
309
+ state TEXT,
310
+ -- Event duration when the operation has completed.
311
+ duration_ms INTEGER,
312
+ -- Node identifier that emitted the event.
313
+ node_id TEXT,
314
+ -- Channel adapter name associated with the event.
315
+ channel TEXT,
316
+ -- Runtime adapter name associated with the event.
317
+ runtime TEXT,
318
+ -- Channel session correlation identifier.
319
+ channel_session_id TEXT,
320
+ -- Bcn session correlation identifier.
321
+ bcn_session_id TEXT,
322
+ -- Agent runtime session correlation identifier.
323
+ runtime_session_id TEXT,
324
+ -- Runtime turn correlation identifier.
325
+ turn_id TEXT,
326
+ -- Provider or protocol request correlation identifier.
327
+ request_id TEXT,
328
+ -- Local command correlation identifier.
329
+ command_id TEXT,
330
+ -- Related inbound message sequence when available.
331
+ inbound_seq INTEGER,
332
+ -- Related outbound message identifier when available.
333
+ outbound_message_id TEXT,
334
+ -- Stable application error category.
335
+ error_kind TEXT,
336
+ -- Runtime error type after redaction.
337
+ error_type TEXT,
338
+ -- Redacted error summary.
339
+ error_message TEXT,
340
+ -- Controlled reference to a retained traceback.
341
+ traceback_ref TEXT,
342
+ -- Non-sensitive event metadata encoded as JSON.
343
+ metadata_json TEXT
344
+ )
345
+ """,
346
+ """
347
+ CREATE INDEX idx_inbound_session_seq
348
+ ON inbound_messages (session_id, seq)
349
+ """,
350
+ """
351
+ CREATE INDEX idx_inbound_seq
352
+ ON inbound_messages (seq)
353
+ """,
354
+ """
355
+ CREATE INDEX idx_inbound_channel_received
356
+ ON inbound_messages (channel_session_id, received_at_ms)
357
+ """,
358
+ """
359
+ CREATE INDEX idx_outbound_session_created
360
+ ON outbound_messages (session_id, created_at_ms)
361
+ """,
362
+ """
363
+ CREATE INDEX idx_outbound_state_created
364
+ ON outbound_messages (state, created_at_ms)
365
+ """,
366
+ """
367
+ CREATE INDEX idx_runtime_turns_session_state
368
+ ON runtime_turns (session_id, state, started_at_ms)
369
+ """,
370
+ """
371
+ CREATE INDEX idx_runtime_events_session_seq
372
+ ON runtime_events (bcn_session_id, event_seq)
373
+ """,
374
+ """
375
+ CREATE INDEX idx_runtime_events_name_seq
376
+ ON runtime_events (event_name, event_seq)
377
+ """,
378
+ """
379
+ CREATE INDEX idx_runtime_events_created
380
+ ON runtime_events (created_at_ms)
381
+ """,
382
+ ),
383
+ )
384
+
385
+ SESSION_MAPPING_INDEX_MIGRATION = Migration(
386
+ version=2,
387
+ name="session_mapping_indexes",
388
+ statements=(
389
+ """
390
+ -- Provider identity lookup used by channel session get-or-create.
391
+ CREATE INDEX idx_channel_sessions_provider_identity
392
+ ON channel_sessions (
393
+ channel,
394
+ provider_thread_id
395
+ )
396
+ """,
397
+ """
398
+ -- Channel-to-bcn session lookup used during recovery reconciliation.
399
+ CREATE INDEX idx_bcn_sessions_channel
400
+ ON bcn_sessions (channel_session_id)
401
+ """,
402
+ """
403
+ -- Bcn-to-runtime session lookup used during process reconciliation.
404
+ CREATE INDEX idx_runtime_sessions_bcn
405
+ ON runtime_sessions (bcn_session_id)
406
+ """,
407
+ ),
408
+ )
409
+
410
+ MESSAGE_LOG_INDEX_MIGRATION = Migration(
411
+ version=3,
412
+ name="message_log_indexes",
413
+ statements=(
414
+ """
415
+ -- Provider-scoped inbound deduplication lookup.
416
+ CREATE INDEX idx_inbound_provider_identity
417
+ ON inbound_messages (channel, provider_message_id)
418
+ """,
419
+ """
420
+ -- Target-filtered history lookup for one bcn session.
421
+ CREATE INDEX idx_inbound_session_target_seq
422
+ ON inbound_messages (session_id, canonical_target, seq)
423
+ """,
424
+ ),
425
+ )
426
+
427
+ RUNTIME_ATTEMPT_FACT_MIGRATION = Migration(
428
+ version=4,
429
+ name="runtime_attempt_facts",
430
+ statements=(
431
+ """
432
+ CREATE TABLE runtime_attempts (
433
+ turn_id TEXT PRIMARY KEY,
434
+ session_id TEXT,
435
+ client_user_message_id TEXT,
436
+ started_at_ms INTEGER
437
+ )
438
+ """,
439
+ """
440
+ INSERT INTO runtime_attempts (
441
+ turn_id,
442
+ session_id,
443
+ client_user_message_id,
444
+ started_at_ms
445
+ )
446
+ SELECT
447
+ turn_id,
448
+ session_id,
449
+ client_user_message_id,
450
+ started_at_ms
451
+ FROM runtime_turns
452
+ WHERE client_user_message_id IS NOT NULL
453
+ """,
454
+ """
455
+ DROP INDEX idx_runtime_turns_session_state
456
+ """,
457
+ """
458
+ DROP TABLE runtime_turns
459
+ """,
460
+ """
461
+ CREATE INDEX idx_runtime_attempts_session_started
462
+ ON runtime_attempts (session_id, started_at_ms)
463
+ """,
464
+ ),
465
+ )
466
+
467
+ INBOUND_MESSAGE_REFERENCE_MIGRATION = Migration(
468
+ version=5,
469
+ name="inbound_message_references",
470
+ statements=(
471
+ """
472
+ ALTER TABLE inbound_messages
473
+ RENAME COLUMN reply_to_provider_message_id TO reply_to_message_id
474
+ """,
475
+ """
476
+ UPDATE inbound_messages AS current
477
+ SET reply_to_message_id = (
478
+ SELECT referenced.message_id
479
+ FROM inbound_messages AS referenced
480
+ WHERE referenced.channel = current.channel
481
+ AND referenced.provider_message_id = current.reply_to_message_id
482
+ ORDER BY referenced.seq
483
+ LIMIT 1
484
+ )
485
+ WHERE current.reply_to_message_id IS NOT NULL
486
+ """,
487
+ """
488
+ CREATE INDEX idx_inbound_reply_to_message
489
+ ON inbound_messages (reply_to_message_id)
490
+ """,
491
+ ),
492
+ )
493
+
494
+ INBOUND_MESSAGE_REFERENCE_INTEGRITY_MIGRATION = Migration(
495
+ version=6,
496
+ name="inbound_message_reference_integrity",
497
+ statements=(
498
+ """
499
+ UPDATE inbound_messages AS current
500
+ SET reply_to_message_id = NULL
501
+ WHERE current.reply_to_message_id IS NOT NULL
502
+ AND NOT EXISTS (
503
+ SELECT 1
504
+ FROM inbound_messages AS referenced
505
+ WHERE referenced.message_id = current.reply_to_message_id
506
+ AND referenced.session_id = current.session_id
507
+ AND referenced.seq < current.seq
508
+ )
509
+ """,
510
+ ),
511
+ )
512
+
513
+ INBOUND_PROVIDER_IDENTITY_MIGRATION = Migration(
514
+ version=7,
515
+ name="inbound_provider_identity",
516
+ statements=(
517
+ """
518
+ DROP INDEX idx_inbound_provider_identity
519
+ """,
520
+ """
521
+ CREATE UNIQUE INDEX idx_inbound_provider_identity
522
+ ON inbound_messages (
523
+ channel,
524
+ provider_thread_id,
525
+ provider_message_id
526
+ )
527
+ """,
528
+ ),
529
+ )
530
+
531
+ TRANSIENT_STREAM_EVENT_MIGRATION = Migration(
532
+ version=8,
533
+ name="transient_stream_events",
534
+ statements=(
535
+ """
536
+ DELETE FROM runtime_events
537
+ WHERE event_name = 'codex.turn.progress'
538
+ AND (
539
+ json_extract(metadata_json, '$.provider_method') = 'turn/progress'
540
+ OR (
541
+ json_extract(metadata_json, '$.provider_method') LIKE 'item/%'
542
+ AND json_extract(metadata_json, '$.provider_method') NOT IN (
543
+ 'item/started',
544
+ 'item/completed',
545
+ 'item/autoApprovalReview/started',
546
+ 'item/autoApprovalReview/completed'
547
+ )
548
+ )
549
+ )
550
+ """,
551
+ ),
552
+ )
553
+
554
+ MIGRATIONS: tuple[Migration, ...] = (
555
+ SCHEMA_MIGRATION,
556
+ SESSION_MAPPING_INDEX_MIGRATION,
557
+ MESSAGE_LOG_INDEX_MIGRATION,
558
+ RUNTIME_ATTEMPT_FACT_MIGRATION,
559
+ INBOUND_MESSAGE_REFERENCE_MIGRATION,
560
+ INBOUND_MESSAGE_REFERENCE_INTEGRITY_MIGRATION,
561
+ INBOUND_PROVIDER_IDENTITY_MIGRATION,
562
+ TRANSIENT_STREAM_EVENT_MIGRATION,
563
+ )
564
+
565
+
566
+ async def apply_migrations(
567
+ transaction: SqliteTransaction,
568
+ *,
569
+ clock: Callable[[], int] = _current_time_ms,
570
+ ) -> int:
571
+ """Apply the ordered migration ledger inside the caller's transaction."""
572
+
573
+ ledger_exists = (
574
+ await transaction.fetchone(
575
+ "SELECT 1 FROM sqlite_master "
576
+ "WHERE type = 'table' AND name = 'schema_migrations'"
577
+ )
578
+ is not None
579
+ )
580
+ applied_rows = []
581
+ if ledger_exists:
582
+ applied_rows = await transaction.fetchall(
583
+ "SELECT version, migration_name, checksum "
584
+ "FROM schema_migrations ORDER BY version"
585
+ )
586
+ known_versions = {migration.version for migration in MIGRATIONS}
587
+ unknown_versions = {
588
+ int(row["version"])
589
+ for row in applied_rows
590
+ if int(row["version"]) not in known_versions
591
+ }
592
+ if unknown_versions:
593
+ raise MigrationError(
594
+ "database contains unknown migration versions: "
595
+ + ", ".join(str(version) for version in sorted(unknown_versions))
596
+ )
597
+
598
+ applied_by_version = {int(row["version"]): row for row in applied_rows}
599
+ preexisting_ledger = ledger_exists
600
+ latest_version = 0
601
+ missing_version = False
602
+ for migration in MIGRATIONS:
603
+ row = applied_by_version.get(migration.version)
604
+ if row is None:
605
+ missing_version = True
606
+ continue
607
+ if missing_version:
608
+ raise MigrationError(
609
+ "migration ledger contains a later version after a missing "
610
+ f"version before {migration.version}"
611
+ )
612
+ if (
613
+ row["migration_name"] != migration.name
614
+ or row["checksum"] != migration.checksum
615
+ ):
616
+ raise MigrationChecksumError(
617
+ f"migration {migration.version} does not match its ledger entry"
618
+ )
619
+ latest_version = migration.version
620
+
621
+ if preexisting_ledger and latest_version == 0:
622
+ raise MigrationError(
623
+ f"migration ledger is missing version {MIGRATIONS[0].version}"
624
+ )
625
+
626
+ for migration in MIGRATIONS:
627
+ if migration.version <= latest_version:
628
+ continue
629
+ started_at_ns = monotonic_ns()
630
+ for statement in migration.statements:
631
+ await transaction.execute(statement)
632
+ await transaction.execute(
633
+ "INSERT INTO schema_migrations "
634
+ "(version, migration_name, checksum, applied_at_ms, duration_ms) "
635
+ "VALUES (?, ?, ?, ?, ?)",
636
+ (
637
+ migration.version,
638
+ migration.name,
639
+ migration.checksum,
640
+ clock(),
641
+ (monotonic_ns() - started_at_ns) // 1_000_000,
642
+ ),
643
+ )
644
+ latest_version = migration.version
645
+
646
+ return latest_version
@@ -0,0 +1,11 @@
1
+ from __future__ import annotations
2
+
3
+ from ...core.storage import IStorage
4
+ from .database import SqliteDatabase
5
+
6
+
7
+ def create_storage() -> IStorage:
8
+ return SqliteDatabase()
9
+
10
+
11
+ __all__ = ["create_storage"]