bazaar-compute-node 0.1.25__tar.gz → 0.1.26__tar.gz

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 (138) hide show
  1. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.26}/PKG-INFO +1 -1
  2. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.26}/pyproject.toml +1 -1
  3. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.26}/pyproject.toml.orig +1 -1
  4. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/app/agent.py +7 -10
  5. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/app/command.py +83 -41
  6. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/contrib/lark/channel.py +10 -8
  7. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/contrib/sqlite/codec.py +134 -109
  8. bazaar_compute_node-0.1.26/src/bazaar_compute_node/contrib/sqlite/database.py +288 -0
  9. bazaar_compute_node-0.1.26/src/bazaar_compute_node/contrib/sqlite/executor.py +544 -0
  10. bazaar_compute_node-0.1.26/src/bazaar_compute_node/contrib/sqlite/migrations/__init__.py +57 -0
  11. bazaar_compute_node-0.1.26/src/bazaar_compute_node/contrib/sqlite/migrations/registry.py +164 -0
  12. bazaar_compute_node-0.1.25/src/bazaar_compute_node/contrib/sqlite/migrations.py → bazaar_compute_node-0.1.26/src/bazaar_compute_node/contrib/sqlite/migrations/v01_initial_node_schema.py +2 -341
  13. bazaar_compute_node-0.1.26/src/bazaar_compute_node/contrib/sqlite/migrations/v02_session_mapping_indexes.py +31 -0
  14. bazaar_compute_node-0.1.26/src/bazaar_compute_node/contrib/sqlite/migrations/v03_message_log_indexes.py +23 -0
  15. bazaar_compute_node-0.1.26/src/bazaar_compute_node/contrib/sqlite/migrations/v04_runtime_attempt_facts.py +46 -0
  16. bazaar_compute_node-0.1.26/src/bazaar_compute_node/contrib/sqlite/migrations/v05_inbound_message_references.py +33 -0
  17. bazaar_compute_node-0.1.26/src/bazaar_compute_node/contrib/sqlite/migrations/v06_inbound_message_reference_integrity.py +25 -0
  18. bazaar_compute_node-0.1.26/src/bazaar_compute_node/contrib/sqlite/migrations/v07_inbound_provider_identity.py +24 -0
  19. bazaar_compute_node-0.1.26/src/bazaar_compute_node/contrib/sqlite/migrations/v08_transient_stream_events.py +29 -0
  20. bazaar_compute_node-0.1.26/src/bazaar_compute_node/contrib/sqlite/migrations/v09_remove_runtime_events.py +20 -0
  21. bazaar_compute_node-0.1.26/src/bazaar_compute_node/contrib/sqlite/migrations/v10_add_outbound_attachments.py +17 -0
  22. bazaar_compute_node-0.1.26/src/bazaar_compute_node/contrib/sqlite/migrations/v11_remove_runtime_session_mapping.py +19 -0
  23. bazaar_compute_node-0.1.25/src/bazaar_compute_node/contrib/sqlite/reminder_migration.py → bazaar_compute_node-0.1.26/src/bazaar_compute_node/contrib/sqlite/migrations/v12_add_reminders.py +1 -1
  24. bazaar_compute_node-0.1.25/src/bazaar_compute_node/contrib/sqlite/agent_migration.py → bazaar_compute_node-0.1.26/src/bazaar_compute_node/contrib/sqlite/migrations/v13_add_agent_ownership.py +1 -1
  25. bazaar_compute_node-0.1.25/src/bazaar_compute_node/contrib/sqlite/inbox_migration.py → bazaar_compute_node-0.1.26/src/bazaar_compute_node/contrib/sqlite/migrations/v14_add_inbox_discovery_indexes.py +1 -1
  26. bazaar_compute_node-0.1.25/src/bazaar_compute_node/contrib/sqlite/handoff_migration.py → bazaar_compute_node-0.1.26/src/bazaar_compute_node/contrib/sqlite/migrations/v15_add_handoffs.py +1 -1
  27. bazaar_compute_node-0.1.25/src/bazaar_compute_node/contrib/sqlite/outbound_draft_migration.py → bazaar_compute_node-0.1.26/src/bazaar_compute_node/contrib/sqlite/migrations/v16_remove_outbound_drafts.py +1 -1
  28. bazaar_compute_node-0.1.26/src/bazaar_compute_node/contrib/sqlite/migrations/v17_remove_agent_identity_triggers.py +20 -0
  29. bazaar_compute_node-0.1.26/src/bazaar_compute_node/contrib/sqlite/migrations/v18_unify_messages.py +400 -0
  30. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/contrib/sqlite/plugin.py +2 -1
  31. bazaar_compute_node-0.1.26/src/bazaar_compute_node/contrib/sqlite/repository/__init__.py +5 -0
  32. bazaar_compute_node-0.1.26/src/bazaar_compute_node/contrib/sqlite/repository/base.py +168 -0
  33. bazaar_compute_node-0.1.26/src/bazaar_compute_node/contrib/sqlite/repository/facade.py +143 -0
  34. bazaar_compute_node-0.1.25/src/bazaar_compute_node/contrib/sqlite/handoff_repository.py → bazaar_compute_node-0.1.26/src/bazaar_compute_node/contrib/sqlite/repository/handoffs.py +8 -34
  35. bazaar_compute_node-0.1.26/src/bazaar_compute_node/contrib/sqlite/repository/messages.py +813 -0
  36. bazaar_compute_node-0.1.25/src/bazaar_compute_node/contrib/sqlite/reminder_repository.py → bazaar_compute_node-0.1.26/src/bazaar_compute_node/contrib/sqlite/repository/reminders.py +221 -233
  37. bazaar_compute_node-0.1.26/src/bazaar_compute_node/contrib/sqlite/repository/sessions.py +272 -0
  38. bazaar_compute_node-0.1.26/src/bazaar_compute_node/contrib/sqlite/storage.py +154 -0
  39. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/contrib/telegram/channel.py +11 -8
  40. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/contrib/wecom/channel.py +13 -10
  41. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/core/channel.py +4 -4
  42. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/core/command.py +13 -9
  43. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/core/models/__init__.py +4 -4
  44. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/core/models/entities.py +116 -62
  45. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/core/models/states.py +5 -0
  46. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/core/orchestration/command.py +64 -239
  47. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/core/orchestration/error_feedback.py +4 -3
  48. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/core/orchestration/handoff_command.py +49 -105
  49. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/core/orchestration/reminder.py +9 -15
  50. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/core/orchestration/reminder_command.py +36 -96
  51. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/core/orchestration/services.py +3 -4
  52. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/core/orchestration/session.py +100 -221
  53. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/core/orchestration/turn.py +8 -8
  54. bazaar_compute_node-0.1.26/src/bazaar_compute_node/core/storage.py +753 -0
  55. bazaar_compute_node-0.1.25/src/bazaar_compute_node/contrib/sqlite/database.py +0 -220
  56. bazaar_compute_node-0.1.25/src/bazaar_compute_node/contrib/sqlite/repository.py +0 -806
  57. bazaar_compute_node-0.1.25/src/bazaar_compute_node/contrib/sqlite/scoped_repository.py +0 -931
  58. bazaar_compute_node-0.1.25/src/bazaar_compute_node/contrib/sqlite/storage.py +0 -74
  59. bazaar_compute_node-0.1.25/src/bazaar_compute_node/core/storage.py +0 -228
  60. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.26}/LICENSE +0 -0
  61. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.26}/README.md +0 -0
  62. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/__init__.py +0 -0
  63. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/app/__init__.py +0 -0
  64. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/app/agent_management.py +0 -0
  65. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/app/application.py +0 -0
  66. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/app/attachments.py +0 -0
  67. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/app/config.py +0 -0
  68. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/app/registry.py +0 -0
  69. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/app/resource_dispatch.py +0 -0
  70. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/app/system_service.py +0 -0
  71. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/app/transport.py +0 -0
  72. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/app/windows_pipe.py +0 -0
  73. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/app/wrapper.py +0 -0
  74. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/bcc.py +0 -0
  75. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/cli.py +0 -0
  76. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/contrib/__init__.py +0 -0
  77. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/contrib/codex/__init__.py +0 -0
  78. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/contrib/codex/approval.py +0 -0
  79. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/contrib/codex/client.py +0 -0
  80. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/contrib/codex/events.py +0 -0
  81. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/contrib/codex/plugin.py +0 -0
  82. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/contrib/codex/process.py +0 -0
  83. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/contrib/codex/protocol.py +0 -0
  84. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/contrib/codex/runtime.py +0 -0
  85. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/contrib/lark/__init__.py +0 -0
  86. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/contrib/lark/api.py +0 -0
  87. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/contrib/lark/approval.py +0 -0
  88. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/contrib/lark/attachments.py +0 -0
  89. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/contrib/lark/frame.py +0 -0
  90. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/contrib/lark/identity.py +0 -0
  91. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/contrib/lark/outbound.py +0 -0
  92. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/contrib/lark/plugin.py +0 -0
  93. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/contrib/lark/transport.py +0 -0
  94. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/contrib/logging/__init__.py +0 -0
  95. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/contrib/logging/audit.py +0 -0
  96. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/contrib/logging/plugin.py +0 -0
  97. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/contrib/sqlite/__init__.py +0 -0
  98. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/contrib/sqlite/handoff_codec.py +0 -0
  99. /bazaar_compute_node-0.1.25/src/bazaar_compute_node/contrib/sqlite/migration.py → /bazaar_compute_node-0.1.26/src/bazaar_compute_node/contrib/sqlite/migrations/model.py +0 -0
  100. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/contrib/sqlite/reminder_codec.py +0 -0
  101. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/contrib/telegram/__init__.py +0 -0
  102. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/contrib/telegram/api.py +0 -0
  103. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/contrib/telegram/approval.py +0 -0
  104. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/contrib/telegram/attachments.py +0 -0
  105. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/contrib/telegram/identity.py +0 -0
  106. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/contrib/telegram/markdown.py +0 -0
  107. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/contrib/telegram/outbound.py +0 -0
  108. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/contrib/telegram/plugin.py +0 -0
  109. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/contrib/wecom/__init__.py +0 -0
  110. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/contrib/wecom/markdown.py +0 -0
  111. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/contrib/wecom/outbound.py +0 -0
  112. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/contrib/wecom/plugin.py +0 -0
  113. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/core/__init__.py +0 -0
  114. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/core/approval.py +0 -0
  115. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/core/audit.py +0 -0
  116. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/core/client.py +0 -0
  117. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/core/concurrency.py +0 -0
  118. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/core/correlation.py +0 -0
  119. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/core/handoff.py +0 -0
  120. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/core/inbox.py +0 -0
  121. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/core/instruction.py +0 -0
  122. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/core/lifecycle.py +0 -0
  123. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/core/models/handoff.py +0 -0
  124. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/core/models/reminder.py +0 -0
  125. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/core/models/reminder_owner.py +0 -0
  126. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/core/observability.py +0 -0
  127. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/core/orchestration/__init__.py +0 -0
  128. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/core/orchestration/delivery.py +0 -0
  129. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/core/outcomes.py +0 -0
  130. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/core/paths.py +0 -0
  131. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/core/reminder.py +0 -0
  132. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/core/reminder_schedule.py +0 -0
  133. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/core/runtime.py +0 -0
  134. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/core/timerwheel.py +0 -0
  135. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/i18n/__init__.py +0 -0
  136. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/i18n/catalog.py +0 -0
  137. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/i18n/english.py +0 -0
  138. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/i18n/schinese.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: bazaar-compute-node
3
- Version: 0.1.25
3
+ Version: 0.1.26
4
4
  Summary: Runtime-agnostic computer node daemon for agents and channels.
5
5
  License-Expression: AGPL-3.0-only
6
6
  License-File: LICENSE
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "bazaar-compute-node"
3
- version = "0.1.25"
3
+ version = "0.1.26"
4
4
  description = "Runtime-agnostic computer node daemon for agents and channels."
5
5
  readme = "README.md"
6
6
  license = "AGPL-3.0-only"
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "bazaar-compute-node"
3
- version = "0.1.25"
3
+ version = "0.1.26"
4
4
  description = "Runtime-agnostic computer node daemon for agents and channels."
5
5
  readme = "README.md"
6
6
  license = "AGPL-3.0-only"
@@ -279,8 +279,7 @@ class AgentApplication:
279
279
  async def has_session(self, session_id: str) -> bool:
280
280
  if not isinstance(session_id, str) or not session_id:
281
281
  return False
282
- async with self.storage.transaction() as transaction:
283
- return await transaction.get_bcn_session(session_id) is not None
282
+ return await self.storage.get_bcn_session(session_id) is not None
284
283
 
285
284
  async def publish_reminder_wake(self, session_id: str) -> None:
286
285
  if not self._started:
@@ -323,8 +322,7 @@ class AgentApplication:
323
322
  self._wrapper_path = None
324
323
 
325
324
  async def _referenced_attachment_paths(self) -> set[str]:
326
- async with self.storage.transaction() as transaction:
327
- return set(await transaction.list_ready_attachment_paths())
325
+ return set(await self.storage.list_ready_attachment_paths())
328
326
 
329
327
  def _adapter_context(self) -> Mapping[str, object]:
330
328
  return {
@@ -370,12 +368,11 @@ class AgentApplication:
370
368
  )
371
369
  runtime_session_id = request.get("runtime_session_id")
372
370
  session_capability = request.get("session_capability")
373
- async with self.storage.transaction() as transaction:
374
- if await transaction.get_bcn_session(session_id) is None:
375
- raise CommandDispatchError(
376
- "SESSION_NOT_FOUND",
377
- f"unknown bcn session: {session_id}",
378
- )
371
+ if await self.storage.get_bcn_session(session_id) is None:
372
+ raise CommandDispatchError(
373
+ "SESSION_NOT_FOUND",
374
+ f"unknown bcn session: {session_id}",
375
+ )
379
376
  runtime_session = self.orchestrator.runtime_session(session_id)
380
377
  if runtime_session is None or runtime_session_id != runtime_session.id:
381
378
  raise CommandDispatchError(
@@ -25,17 +25,54 @@ from ..core.command import (
25
25
  SessionNotFoundError,
26
26
  )
27
27
  from ..core.lifecycle import TimeoutBudget
28
- from ..core.models import InboundMessage, InboxTargetSummary, OutboundDeliveryState
28
+ from ..core.models import (
29
+ InboundAttachment,
30
+ InboxTargetSummary,
31
+ Message,
32
+ MessageDirection,
33
+ OutboundAttachment,
34
+ OutboundDeliveryState,
35
+ )
29
36
 
30
37
 
31
- def serialize_inbound(message: InboundMessage) -> dict[str, object]:
38
+ def _serialize_attachment(
39
+ attachment: InboundAttachment | OutboundAttachment,
40
+ ) -> dict[str, object]:
41
+ if isinstance(attachment, InboundAttachment):
42
+ return {
43
+ "attachment_id": attachment.attachment_id,
44
+ "name": attachment.name,
45
+ "kind": attachment.kind,
46
+ "state": attachment.state,
47
+ "media_type": attachment.media_type,
48
+ "relative_path": attachment.relative_path,
49
+ "size_bytes": attachment.size_bytes,
50
+ "error": attachment.error,
51
+ "sha256": None,
52
+ }
53
+ return {
54
+ "attachment_id": None,
55
+ "name": attachment.name,
56
+ "kind": "file",
57
+ "state": "ready",
58
+ "media_type": attachment.media_type,
59
+ "relative_path": attachment.relative_path,
60
+ "size_bytes": attachment.size_bytes,
61
+ "error": None,
62
+ "sha256": attachment.sha256,
63
+ }
64
+
65
+
66
+ def serialize_message(message: Message) -> dict[str, object]:
32
67
  return {
33
68
  "seq": message.seq,
34
69
  "message_id": message.message_id,
70
+ "direction": message.direction.value,
35
71
  "session_id": message.session_id,
36
72
  "channel_session_id": message.channel_session_id,
37
73
  "channel": message.channel,
38
74
  "received_at_ms": message.received_at_ms,
75
+ "created_at_ms": message.created_at_ms,
39
76
  "provider_time_ms": message.provider_time_ms,
40
77
  "sender": (
41
78
  None
@@ -44,25 +81,21 @@ def serialize_inbound(message: InboundMessage) -> dict[str, object]:
44
81
  ),
45
82
  "sender_kind": message.sender_kind.value,
46
83
  "message_type": message.message_type,
47
- "canonical_target": message.canonical_target,
84
+ "canonical_target": message.target,
48
85
  "target_kind": message.target_kind.value,
49
86
  "mentions_agent": message.mentions_agent,
50
87
  "notifies_runtime": message.notifies_runtime,
51
88
  "attachments": [
52
- {
53
- "attachment_id": attachment.attachment_id,
54
- "name": attachment.name,
55
- "kind": attachment.kind,
56
- "state": attachment.state,
57
- "media_type": attachment.media_type,
58
- "relative_path": attachment.relative_path,
59
- "size_bytes": attachment.size_bytes,
60
- "error": attachment.error,
61
- }
62
- for attachment in message.attachments
89
+ _serialize_attachment(attachment) for attachment in message.attachments
63
90
  ],
64
91
  "body": message.body,
65
92
  "reply_to_message_id": message.reply_to_message_id,
93
+ "delivery_state": (
94
+ message.delivery_state.value
95
+ if message.direction is MessageDirection.OUTBOUND
96
+ and message.delivery_state is not None
97
+ else None
98
+ ),
66
99
  }
67
100
 
68
101
 
@@ -404,10 +437,10 @@ class CommandDispatcher:
404
437
  "ok": True,
405
438
  "result": {
406
439
  "messages": [
407
- serialize_inbound(message) for message in result.messages
440
+ serialize_message(message) for message in result.messages
408
441
  ],
409
442
  "referenced_messages": [
410
- serialize_inbound(message)
443
+ serialize_message(message)
411
444
  for message in result.referenced_messages
412
445
  ],
413
446
  "snapshot_seq": result.snapshot_seq,
@@ -436,10 +469,10 @@ class CommandDispatcher:
436
469
  "ok": True,
437
470
  "result": {
438
471
  "messages": [
439
- serialize_inbound(message) for message in result.messages
472
+ serialize_message(message) for message in result.messages
440
473
  ],
441
474
  "referenced_messages": [
442
- serialize_inbound(message)
475
+ serialize_message(message)
443
476
  for message in result.referenced_messages
444
477
  ],
445
478
  "snapshot_seq": result.snapshot_seq,
@@ -470,17 +503,19 @@ class CommandDispatcher:
470
503
  "ok": True,
471
504
  "result": {"text": format_cross_session_hold(result.target)},
472
505
  }
473
- if result.state is OutboundDeliveryState.SENT:
506
+ delivery_state = result.delivery_state
507
+ if delivery_state is None:
508
+ raise RuntimeError("outbound message has no delivery state")
509
+ if delivery_state is OutboundDeliveryState.SENT:
474
510
  text = (
475
- f"Message sent to {result.target}. "
476
- f"Message ID: {result.outbound_message_id}"
511
+ f"Message sent to {result.target}. Message ID: {result.message_id}"
477
512
  )
478
- elif result.state is OutboundDeliveryState.QUEUED:
513
+ elif delivery_state is OutboundDeliveryState.QUEUED:
479
514
  text = (
480
515
  f"Message queued to {result.target}. "
481
- f"Message ID: {result.outbound_message_id}"
516
+ f"Message ID: {result.message_id}"
482
517
  )
483
- elif result.state is OutboundDeliveryState.PARTIAL:
518
+ elif delivery_state is OutboundDeliveryState.PARTIAL:
484
519
  raise CommandDispatchError(
485
520
  "SEND_PARTIAL",
486
521
  result.error_message
@@ -490,13 +525,13 @@ class CommandDispatcher:
490
525
  "confirmed delivery first."
491
526
  ),
492
527
  )
493
- elif result.state is OutboundDeliveryState.UNKNOWN:
528
+ elif delivery_state is OutboundDeliveryState.UNKNOWN:
494
529
  raise CommandDispatchError(
495
530
  "SEND_UNKNOWN",
496
531
  result.error_message or "Message delivery outcome is unknown.",
497
532
  next_action="Reconcile channel delivery before retrying.",
498
533
  )
499
- elif result.state is OutboundDeliveryState.FAILED:
534
+ elif delivery_state is OutboundDeliveryState.FAILED:
500
535
  raise CommandDispatchError(
501
536
  "SEND_FAILED",
502
537
  result.error_message or "Message delivery failed.",
@@ -504,7 +539,7 @@ class CommandDispatcher:
504
539
  )
505
540
  else:
506
541
  raise AssertionError(
507
- f"message send returned unsupported state: {result.state.value}"
542
+ f"message send returned unsupported state: {delivery_state.value}"
508
543
  )
509
544
  return {
510
545
  "ok": True,
@@ -528,10 +563,11 @@ def format_message_time(timestamp_ms: int) -> str:
528
563
 
529
564
 
530
565
  def _message_timestamp(message: Mapping[str, object]) -> int:
531
- timestamp = message["provider_time_ms"]
532
- if timestamp is None:
533
- timestamp = message["received_at_ms"]
534
- return cast(int, timestamp)
566
+ for field_name in ("provider_time_ms", "received_at_ms", "created_at_ms"):
567
+ timestamp = message.get(field_name)
568
+ if timestamp is not None:
569
+ return cast(int, timestamp)
570
+ raise ValueError("message has no display timestamp")
535
571
 
536
572
 
537
573
  def _message_header_fields(
@@ -546,7 +582,12 @@ def _message_header_fields(
546
582
  sender_mapping = cast(Mapping[str, object], sender_value)
547
583
  sender_id = cast(str | None, sender_mapping.get("id"))
548
584
  sender_name = cast(str | None, sender_mapping.get("name"))
549
- sender = f"@{sender_id}({sender_name})" if sender_name else f"@{sender_id}"
585
+ if sender_id is not None and sender_name is not None:
586
+ sender = f"@{sender_id}({sender_name})"
587
+ elif sender_name is not None:
588
+ sender = f"@{sender_name}"
589
+ elif sender_id is not None:
590
+ sender = f"@{sender_id}"
550
591
  return (
551
592
  target,
552
593
  message_id,
@@ -564,25 +605,26 @@ def _attachment_suffix(message: Mapping[str, object]) -> str:
564
605
  rendered: list[str] = []
565
606
  for attachment in attachments:
566
607
  name = cast(str, attachment["name"])
567
- attachment_id = cast(str, attachment["attachment_id"])
608
+ attachment_id = cast(str | None, attachment.get("attachment_id"))
568
609
  state = cast(str, attachment["state"])
610
+ identity = f"id:{attachment_id}, " if attachment_id is not None else ""
569
611
  if state == "ready":
570
612
  path = cast(str, attachment["relative_path"])
571
- rendered.append(f"{name} (id:{attachment_id}, path:{path})")
613
+ rendered.append(f"{name} ({identity}path:{path})")
572
614
  else:
573
615
  error = cast(str, attachment["error"])
574
- rendered.append(f"{name} (id:{attachment_id}, state:failed, error:{error})")
616
+ rendered.append(f"{name} ({identity}state:failed, error:{error})")
575
617
  label = "attachment" if len(rendered) == 1 else "attachments"
576
618
  return f" [{len(rendered)} {label}: {', '.join(rendered)}]"
577
619
 
578
620
 
579
621
  def format_check_message(message: Mapping[str, object]) -> str:
580
- target, message_id, timestamp, message_type, sender, body = _message_header_fields(
622
+ target, message_id, timestamp, sender_kind, sender, body = _message_header_fields(
581
623
  message
582
624
  )
583
625
  line = (
584
626
  f"[target={target} msg={message_id} time={timestamp} "
585
- f"type={message_type} mentioned={str(message['mentions_agent']).lower()}"
627
+ f"type={sender_kind} mentioned={str(message['mentions_agent']).lower()}"
586
628
  )
587
629
  reply_to_message_id = message["reply_to_message_id"]
588
630
  if reply_to_message_id is not None:
@@ -599,14 +641,14 @@ def format_read_message(
599
641
  index: int,
600
642
  count: int,
601
643
  ) -> str:
602
- target, message_id, timestamp, message_type, sender, body = _message_header_fields(
644
+ target, message_id, timestamp, sender_kind, sender, body = _message_header_fields(
603
645
  message
604
646
  )
605
647
  fields = [
606
648
  f"seq={cast(int, message['seq'])}",
607
649
  f"msg={message_id}",
608
650
  f"time={timestamp}",
609
- f"type={message_type}",
651
+ f"type={sender_kind}",
610
652
  f"replyTarget={target}",
611
653
  f"mentioned={str(message['mentions_agent']).lower()}",
612
654
  ]
@@ -620,9 +662,9 @@ def format_read_message(
620
662
 
621
663
 
622
664
  def format_freshness_hold(result: MessageSendFreshnessHold) -> str:
623
- messages = [serialize_inbound(message) for message in result.messages]
665
+ messages = [serialize_message(message) for message in result.messages]
624
666
  referenced_messages = [
625
- serialize_inbound(message) for message in result.referenced_messages
667
+ serialize_message(message) for message in result.referenced_messages
626
668
  ]
627
669
  shown = len(messages)
628
670
  total = result.newer_message_total
@@ -23,7 +23,8 @@ from ...core.models import (
23
23
  ApprovalResult,
24
24
  ChannelTargetKind,
25
25
  InboundAttachment,
26
- InboundMessage,
26
+ Message,
27
+ MessageDirection,
27
28
  RuntimeEvent,
28
29
  RuntimeEventState,
29
30
  SenderIdentity,
@@ -90,7 +91,7 @@ class LarkChannel(IChannel):
90
91
  self._region = region
91
92
  self._base_url = base_url
92
93
  self._timer_wheel = timer_wheel
93
- self._inbound: asyncio.Queue[InboundMessage | object] = asyncio.Queue()
94
+ self._inbound: asyncio.Queue[Message | object] = asyncio.Queue()
94
95
  self._lifecycle_lock = asyncio.Lock()
95
96
  self._session: aiohttp.ClientSession | None = None
96
97
  self._api: LarkApi | None = None
@@ -279,13 +280,13 @@ class LarkChannel(IChannel):
279
280
  self._inbound.put_nowait(_STOP)
280
281
  self._stop_sent = True
281
282
 
282
- async def receive(self) -> AsyncIterator[InboundMessage]:
283
+ async def receive(self) -> AsyncIterator[Message]:
283
284
  inbound = self._inbound
284
285
  while True:
285
286
  item = await inbound.get()
286
287
  if item is _STOP:
287
288
  return
288
- if not isinstance(item, InboundMessage):
289
+ if not isinstance(item, Message):
289
290
  raise TypeError("Lark inbound queue contained an invalid message")
290
291
  yield item
291
292
 
@@ -388,7 +389,7 @@ class LarkChannel(IChannel):
388
389
  if root_id is not None:
389
390
  metadata["lark_root_id"] = root_id
390
391
 
391
- quoted: InboundMessage | None = None
392
+ quoted: Message | None = None
392
393
  if parent_id is not None:
393
394
  parent, failure = await self._fetch_parent(
394
395
  parent_id,
@@ -458,7 +459,7 @@ class LarkChannel(IChannel):
458
459
  received_at_ms: int,
459
460
  reply_to_message_id: str | None = None,
460
461
  provider_payload_metadata: Mapping[str, object] | None = None,
461
- ) -> InboundMessage:
462
+ ) -> Message:
462
463
  provider_message_id = _provider_text(message.get("message_id"))
463
464
  raw_message_type = _provider_text(message.get("message_type"))
464
465
  if provider_message_id is None or raw_message_type is None:
@@ -496,7 +497,8 @@ class LarkChannel(IChannel):
496
497
  metadata.setdefault("sender_kind", sender_kind.value)
497
498
  if projection.content_error:
498
499
  metadata["content_parse_failed"] = True
499
- return InboundMessage(
500
+ return Message(
501
+ direction=MessageDirection.INBOUND,
500
502
  seq=0,
501
503
  message_id=thread_identity.message_id(provider_message_id),
502
504
  session_id=thread_identity.session_id,
@@ -507,7 +509,7 @@ class LarkChannel(IChannel):
507
509
  received_at_ms=received_at_ms,
508
510
  sender=sender,
509
511
  message_type=projection.message_type,
510
- canonical_target=f"{target_kind.value}:{thread_identity.channel_session_id}",
512
+ target=f"{target_kind.value}:{thread_identity.channel_session_id}",
511
513
  body=projection.body,
512
514
  target_kind=target_kind,
513
515
  mentions_agent=mentions_agent,