bazaar-compute-node 0.1.25__tar.gz → 0.1.27__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 (143) hide show
  1. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/PKG-INFO +1 -1
  2. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/pyproject.toml +1 -1
  3. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/pyproject.toml.orig +1 -1
  4. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/app/agent.py +11 -36
  5. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/app/application.py +10 -5
  6. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/app/command.py +128 -74
  7. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/app/resource_dispatch.py +3 -197
  8. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/bcc.py +5 -216
  9. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/contrib/lark/channel.py +12 -8
  10. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/contrib/sqlite/codec.py +132 -161
  11. bazaar_compute_node-0.1.27/src/bazaar_compute_node/contrib/sqlite/database.py +288 -0
  12. bazaar_compute_node-0.1.27/src/bazaar_compute_node/contrib/sqlite/executor.py +544 -0
  13. bazaar_compute_node-0.1.27/src/bazaar_compute_node/contrib/sqlite/migrations/__init__.py +59 -0
  14. bazaar_compute_node-0.1.27/src/bazaar_compute_node/contrib/sqlite/migrations/registry.py +172 -0
  15. bazaar_compute_node-0.1.25/src/bazaar_compute_node/contrib/sqlite/migrations.py → bazaar_compute_node-0.1.27/src/bazaar_compute_node/contrib/sqlite/migrations/v01_initial_node_schema.py +2 -341
  16. bazaar_compute_node-0.1.27/src/bazaar_compute_node/contrib/sqlite/migrations/v02_session_mapping_indexes.py +31 -0
  17. bazaar_compute_node-0.1.27/src/bazaar_compute_node/contrib/sqlite/migrations/v03_message_log_indexes.py +23 -0
  18. bazaar_compute_node-0.1.27/src/bazaar_compute_node/contrib/sqlite/migrations/v04_runtime_attempt_facts.py +46 -0
  19. bazaar_compute_node-0.1.27/src/bazaar_compute_node/contrib/sqlite/migrations/v05_inbound_message_references.py +33 -0
  20. bazaar_compute_node-0.1.27/src/bazaar_compute_node/contrib/sqlite/migrations/v06_inbound_message_reference_integrity.py +25 -0
  21. bazaar_compute_node-0.1.27/src/bazaar_compute_node/contrib/sqlite/migrations/v07_inbound_provider_identity.py +24 -0
  22. bazaar_compute_node-0.1.27/src/bazaar_compute_node/contrib/sqlite/migrations/v08_transient_stream_events.py +29 -0
  23. bazaar_compute_node-0.1.27/src/bazaar_compute_node/contrib/sqlite/migrations/v09_remove_runtime_events.py +20 -0
  24. bazaar_compute_node-0.1.27/src/bazaar_compute_node/contrib/sqlite/migrations/v10_add_outbound_attachments.py +17 -0
  25. bazaar_compute_node-0.1.27/src/bazaar_compute_node/contrib/sqlite/migrations/v11_remove_runtime_session_mapping.py +19 -0
  26. bazaar_compute_node-0.1.25/src/bazaar_compute_node/contrib/sqlite/reminder_migration.py → bazaar_compute_node-0.1.27/src/bazaar_compute_node/contrib/sqlite/migrations/v12_add_reminders.py +1 -1
  27. bazaar_compute_node-0.1.25/src/bazaar_compute_node/contrib/sqlite/agent_migration.py → bazaar_compute_node-0.1.27/src/bazaar_compute_node/contrib/sqlite/migrations/v13_add_agent_ownership.py +1 -1
  28. bazaar_compute_node-0.1.25/src/bazaar_compute_node/contrib/sqlite/inbox_migration.py → bazaar_compute_node-0.1.27/src/bazaar_compute_node/contrib/sqlite/migrations/v14_add_inbox_discovery_indexes.py +1 -1
  29. bazaar_compute_node-0.1.25/src/bazaar_compute_node/contrib/sqlite/handoff_migration.py → bazaar_compute_node-0.1.27/src/bazaar_compute_node/contrib/sqlite/migrations/v15_add_handoffs.py +1 -1
  30. bazaar_compute_node-0.1.25/src/bazaar_compute_node/contrib/sqlite/outbound_draft_migration.py → bazaar_compute_node-0.1.27/src/bazaar_compute_node/contrib/sqlite/migrations/v16_remove_outbound_drafts.py +1 -1
  31. bazaar_compute_node-0.1.27/src/bazaar_compute_node/contrib/sqlite/migrations/v17_remove_agent_identity_triggers.py +20 -0
  32. bazaar_compute_node-0.1.27/src/bazaar_compute_node/contrib/sqlite/migrations/v18_unify_messages.py +400 -0
  33. bazaar_compute_node-0.1.27/src/bazaar_compute_node/contrib/sqlite/migrations/v19_reminder_system_messages.py +226 -0
  34. bazaar_compute_node-0.1.27/src/bazaar_compute_node/contrib/sqlite/migrations/v20_remove_freshness_persistence.py +162 -0
  35. bazaar_compute_node-0.1.27/src/bazaar_compute_node/contrib/sqlite/migrations/v21_remove_handoffs.py +14 -0
  36. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/contrib/sqlite/plugin.py +2 -1
  37. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/contrib/sqlite/reminder_codec.py +2 -19
  38. bazaar_compute_node-0.1.27/src/bazaar_compute_node/contrib/sqlite/repository/__init__.py +5 -0
  39. bazaar_compute_node-0.1.27/src/bazaar_compute_node/contrib/sqlite/repository/base.py +187 -0
  40. bazaar_compute_node-0.1.27/src/bazaar_compute_node/contrib/sqlite/repository/facade.py +141 -0
  41. bazaar_compute_node-0.1.27/src/bazaar_compute_node/contrib/sqlite/repository/messages.py +921 -0
  42. bazaar_compute_node-0.1.27/src/bazaar_compute_node/contrib/sqlite/repository/reminders.py +386 -0
  43. bazaar_compute_node-0.1.27/src/bazaar_compute_node/contrib/sqlite/repository/sessions.py +261 -0
  44. bazaar_compute_node-0.1.27/src/bazaar_compute_node/contrib/sqlite/storage.py +141 -0
  45. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/contrib/telegram/channel.py +13 -8
  46. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/contrib/wecom/channel.py +13 -10
  47. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/core/channel.py +4 -4
  48. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/core/command.py +31 -49
  49. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/core/instruction.py +18 -53
  50. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/core/models/__init__.py +8 -10
  51. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/core/models/entities.py +102 -190
  52. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/core/models/reminder.py +0 -76
  53. bazaar_compute_node-0.1.27/src/bazaar_compute_node/core/models/reminder_owner.py +28 -0
  54. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/core/models/states.py +13 -18
  55. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/core/orchestration/command.py +166 -291
  56. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/core/orchestration/error_feedback.py +6 -3
  57. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/core/orchestration/reminder.py +71 -48
  58. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/core/orchestration/reminder_command.py +34 -118
  59. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/core/orchestration/services.py +3 -4
  60. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/core/orchestration/session.py +112 -331
  61. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/core/orchestration/turn.py +70 -31
  62. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/core/reminder.py +25 -37
  63. bazaar_compute_node-0.1.27/src/bazaar_compute_node/core/storage.py +688 -0
  64. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/i18n/english.py +0 -1
  65. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/i18n/schinese.py +0 -1
  66. bazaar_compute_node-0.1.25/src/bazaar_compute_node/contrib/sqlite/database.py +0 -220
  67. bazaar_compute_node-0.1.25/src/bazaar_compute_node/contrib/sqlite/handoff_codec.py +0 -24
  68. bazaar_compute_node-0.1.25/src/bazaar_compute_node/contrib/sqlite/handoff_repository.py +0 -166
  69. bazaar_compute_node-0.1.25/src/bazaar_compute_node/contrib/sqlite/reminder_repository.py +0 -588
  70. bazaar_compute_node-0.1.25/src/bazaar_compute_node/contrib/sqlite/repository.py +0 -806
  71. bazaar_compute_node-0.1.25/src/bazaar_compute_node/contrib/sqlite/scoped_repository.py +0 -931
  72. bazaar_compute_node-0.1.25/src/bazaar_compute_node/contrib/sqlite/storage.py +0 -74
  73. bazaar_compute_node-0.1.25/src/bazaar_compute_node/core/handoff.py +0 -49
  74. bazaar_compute_node-0.1.25/src/bazaar_compute_node/core/models/handoff.py +0 -26
  75. bazaar_compute_node-0.1.25/src/bazaar_compute_node/core/models/reminder_owner.py +0 -59
  76. bazaar_compute_node-0.1.25/src/bazaar_compute_node/core/orchestration/handoff_command.py +0 -301
  77. bazaar_compute_node-0.1.25/src/bazaar_compute_node/core/storage.py +0 -228
  78. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/LICENSE +0 -0
  79. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/README.md +0 -0
  80. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/__init__.py +0 -0
  81. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/app/__init__.py +0 -0
  82. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/app/agent_management.py +0 -0
  83. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/app/attachments.py +0 -0
  84. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/app/config.py +0 -0
  85. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/app/registry.py +0 -0
  86. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/app/system_service.py +0 -0
  87. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/app/transport.py +0 -0
  88. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/app/windows_pipe.py +0 -0
  89. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/app/wrapper.py +0 -0
  90. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/cli.py +0 -0
  91. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/contrib/__init__.py +0 -0
  92. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/contrib/codex/__init__.py +0 -0
  93. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/contrib/codex/approval.py +0 -0
  94. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/contrib/codex/client.py +0 -0
  95. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/contrib/codex/events.py +0 -0
  96. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/contrib/codex/plugin.py +0 -0
  97. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/contrib/codex/process.py +0 -0
  98. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/contrib/codex/protocol.py +0 -0
  99. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/contrib/codex/runtime.py +0 -0
  100. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/contrib/lark/__init__.py +0 -0
  101. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/contrib/lark/api.py +0 -0
  102. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/contrib/lark/approval.py +0 -0
  103. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/contrib/lark/attachments.py +0 -0
  104. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/contrib/lark/frame.py +0 -0
  105. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/contrib/lark/identity.py +0 -0
  106. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/contrib/lark/outbound.py +0 -0
  107. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/contrib/lark/plugin.py +0 -0
  108. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/contrib/lark/transport.py +0 -0
  109. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/contrib/logging/__init__.py +0 -0
  110. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/contrib/logging/audit.py +0 -0
  111. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/contrib/logging/plugin.py +0 -0
  112. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/contrib/sqlite/__init__.py +0 -0
  113. /bazaar_compute_node-0.1.25/src/bazaar_compute_node/contrib/sqlite/migration.py → /bazaar_compute_node-0.1.27/src/bazaar_compute_node/contrib/sqlite/migrations/model.py +0 -0
  114. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/contrib/telegram/__init__.py +0 -0
  115. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/contrib/telegram/api.py +0 -0
  116. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/contrib/telegram/approval.py +0 -0
  117. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/contrib/telegram/attachments.py +0 -0
  118. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/contrib/telegram/identity.py +0 -0
  119. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/contrib/telegram/markdown.py +0 -0
  120. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/contrib/telegram/outbound.py +0 -0
  121. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/contrib/telegram/plugin.py +0 -0
  122. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/contrib/wecom/__init__.py +0 -0
  123. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/contrib/wecom/markdown.py +0 -0
  124. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/contrib/wecom/outbound.py +0 -0
  125. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/contrib/wecom/plugin.py +0 -0
  126. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/core/__init__.py +0 -0
  127. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/core/approval.py +0 -0
  128. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/core/audit.py +0 -0
  129. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/core/client.py +0 -0
  130. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/core/concurrency.py +0 -0
  131. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/core/correlation.py +0 -0
  132. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/core/inbox.py +0 -0
  133. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/core/lifecycle.py +0 -0
  134. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/core/observability.py +0 -0
  135. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/core/orchestration/__init__.py +0 -0
  136. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/core/orchestration/delivery.py +0 -0
  137. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/core/outcomes.py +0 -0
  138. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/core/paths.py +0 -0
  139. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/core/reminder_schedule.py +0 -0
  140. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/core/runtime.py +0 -0
  141. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/core/timerwheel.py +0 -0
  142. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/i18n/__init__.py +0 -0
  143. {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/i18n/catalog.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.27
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.27"
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.27"
4
4
  description = "Runtime-agnostic computer node daemon for agents and channels."
5
5
  readme = "README.md"
6
6
  license = "AGPL-3.0-only"
@@ -10,21 +10,17 @@ import secrets
10
10
  from collections.abc import Callable, Mapping, Sequence
11
11
  from dataclasses import dataclass
12
12
  from pathlib import Path
13
- from time import time_ns
14
- from typing import cast
15
13
 
16
14
  from ..core.channel import AgentScopedChannel, ChannelContext, IChannel
17
15
  from ..core.concurrency import ISessionConcurrency, SessionLockRegistry
18
16
  from ..core.lifecycle import TimeoutBudget
19
- from ..core.models import RuntimeSession
17
+ from ..core.models import InboundAttachment, Message, RuntimeSession
20
18
  from ..core.observability import IAudit
21
19
  from ..core.orchestration import SessionOrchestrator
22
- from ..core.orchestration.handoff_command import HandoffCommandService
23
20
  from ..core.orchestration.reminder_command import ReminderCommandService
24
- from ..core.orchestration.services import SessionAuditRecorder
25
21
  from ..core.paths import resolve_workspace_dir
26
22
  from ..core.runtime import IRuntime, RuntimeCommandContext
27
- from ..core.storage import IHandoffStorageScope, IStorageScope
23
+ from ..core.storage import IStorageScope
28
24
  from ..core.timerwheel import TimerWheel
29
25
  from ..i18n import Translator
30
26
  from .attachments import AttachmentMaterializer
@@ -82,7 +78,6 @@ class AgentApplication:
82
78
  self.agent_id = configuration.id
83
79
  self.name = configuration.name
84
80
  self.storage = storage
85
- self._handoff_storage = cast(IHandoffStorageScope, storage)
86
81
  self.audit = audit
87
82
  self.timer_wheel = timer_wheel
88
83
  self.timeout_budget = timeout_budget
@@ -152,7 +147,6 @@ class AgentApplication:
152
147
  channel=self.channel,
153
148
  runtime=self.runtime,
154
149
  storage=self.storage,
155
- handoff_storage=self._handoff_storage,
156
150
  audit=self.audit,
157
151
  timeout_budget=self.timeout_budget,
158
152
  timer_wheel=self.timer_wheel,
@@ -167,16 +161,6 @@ class AgentApplication:
167
161
  concurrency=reminder_concurrency,
168
162
  poke=reminder_poke,
169
163
  )
170
- self.handoff_service = HandoffCommandService(
171
- storage=self._handoff_storage,
172
- audit=SessionAuditRecorder(
173
- sink=self.audit,
174
- timeout_budget=self.timeout_budget,
175
- clock=lambda: time_ns() // 1_000_000,
176
- ),
177
- publish_wake=self.publish_handoff_wake,
178
- node_id=lambda: self.agent_id,
179
- )
180
164
  self._provider_control_handler = (
181
165
  factories.control(self._adapter_context())
182
166
  if factories.control is not None
@@ -185,7 +169,6 @@ class AgentApplication:
185
169
  self.command_dispatcher = CommandDispatcher(
186
170
  self.orchestrator.command_service,
187
171
  reminder_service=self.reminder_service,
188
- handoff_service=self.handoff_service,
189
172
  timeout_budget=self.timeout_budget,
190
173
  control_handler=(
191
174
  self._handle_control
@@ -279,18 +262,12 @@ class AgentApplication:
279
262
  async def has_session(self, session_id: str) -> bool:
280
263
  if not isinstance(session_id, str) or not session_id:
281
264
  return False
282
- async with self.storage.transaction() as transaction:
283
- return await transaction.get_bcn_session(session_id) is not None
265
+ return await self.storage.get_bcn_session(session_id) is not None
284
266
 
285
- async def publish_reminder_wake(self, session_id: str) -> None:
267
+ async def publish_inbox_wake(self, message: Message[InboundAttachment]) -> None:
286
268
  if not self._started:
287
269
  raise RuntimeError("Agent application is not started")
288
- await self.orchestrator.publish_reminder_wake(session_id)
289
-
290
- async def publish_handoff_wake(self, session_id: str) -> None:
291
- if not self._started:
292
- raise RuntimeError("Agent application is not started")
293
- await self.orchestrator.publish_handoff_wake(session_id)
270
+ await self.orchestrator.publish_inbox_wake(message)
294
271
 
295
272
  def health_record(self) -> dict[str, object]:
296
273
  return {
@@ -323,8 +300,7 @@ class AgentApplication:
323
300
  self._wrapper_path = None
324
301
 
325
302
  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())
303
+ return set(await self.storage.list_ready_attachment_paths())
328
304
 
329
305
  def _adapter_context(self) -> Mapping[str, object]:
330
306
  return {
@@ -370,12 +346,11 @@ class AgentApplication:
370
346
  )
371
347
  runtime_session_id = request.get("runtime_session_id")
372
348
  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
- )
349
+ if await self.storage.get_bcn_session(session_id) is None:
350
+ raise CommandDispatchError(
351
+ "SESSION_NOT_FOUND",
352
+ f"unknown bcn session: {session_id}",
353
+ )
379
354
  runtime_session = self.orchestrator.runtime_session(session_id)
380
355
  if runtime_session is None or runtime_session_id != runtime_session.id:
381
356
  raise CommandDispatchError(
@@ -11,6 +11,7 @@ from pathlib import Path
11
11
 
12
12
  from ..core.concurrency import SessionLockRegistry
13
13
  from ..core.lifecycle import TimeoutBudget
14
+ from ..core.models import InboundAttachment, Message
14
15
  from ..core.observability import IAudit
15
16
  from ..core.orchestration import ReminderScheduler
16
17
  from ..core.paths import resolve_data_dir
@@ -77,7 +78,7 @@ class NodeApplication:
77
78
  storage=self.storage,
78
79
  timer_wheel=self.timer_wheel,
79
80
  concurrency=self._reminder_concurrency,
80
- publish_wake=self._publish_reminder_wake,
81
+ publish_wake=self._publish_inbox_wake,
81
82
  )
82
83
  self.command_server = LocalCommandServer(
83
84
  self._dispatch,
@@ -246,24 +247,28 @@ class NodeApplication:
246
247
  pass
247
248
  await self._stopped.wait()
248
249
 
249
- async def _publish_reminder_wake(self, agent_id: str, session_id: str) -> bool:
250
+ async def _publish_inbox_wake(
251
+ self,
252
+ agent_id: str,
253
+ message: Message[InboundAttachment],
254
+ ) -> bool:
250
255
  agent = self.agents.get(agent_id)
251
256
  if agent is None or not agent.started:
252
257
  self._log(
253
258
  "reminder.wake.agent_unavailable",
254
259
  agent_id=agent_id,
255
- owner_session_id=session_id,
260
+ owner_session_id=message.session_id,
256
261
  )
257
262
  return False
258
263
  try:
259
- await agent.publish_reminder_wake(session_id)
264
+ await agent.publish_inbox_wake(message)
260
265
  except asyncio.CancelledError:
261
266
  raise
262
267
  except Exception as error: # noqa: BLE001
263
268
  self._log(
264
269
  "reminder.wake.failed",
265
270
  agent_id=agent_id,
266
- owner_session_id=session_id,
271
+ owner_session_id=message.session_id,
267
272
  error_type=type(error).__name__,
268
273
  error=_safe_error(error),
269
274
  )
@@ -21,21 +21,57 @@ from ..core.command import (
21
21
  ICommandService,
22
22
  InboxListResult,
23
23
  MessageSendFreshnessHold,
24
- MessageSendHandoffRequired,
25
24
  SessionNotFoundError,
26
25
  )
27
26
  from ..core.lifecycle import TimeoutBudget
28
- from ..core.models import InboundMessage, InboxTargetSummary, OutboundDeliveryState
27
+ from ..core.models import (
28
+ InboundAttachment,
29
+ InboxTargetSummary,
30
+ Message,
31
+ MessageDirection,
32
+ OutboundAttachment,
33
+ OutboundDeliveryState,
34
+ )
35
+
36
+
37
+ def _serialize_attachment(
38
+ attachment: InboundAttachment | OutboundAttachment,
39
+ ) -> dict[str, object]:
40
+ if isinstance(attachment, InboundAttachment):
41
+ return {
42
+ "attachment_id": attachment.attachment_id,
43
+ "name": attachment.name,
44
+ "kind": attachment.kind,
45
+ "state": attachment.state,
46
+ "media_type": attachment.media_type,
47
+ "relative_path": attachment.relative_path,
48
+ "size_bytes": attachment.size_bytes,
49
+ "error": attachment.error,
50
+ "sha256": None,
51
+ }
52
+ return {
53
+ "attachment_id": None,
54
+ "name": attachment.name,
55
+ "kind": "file",
56
+ "state": "ready",
57
+ "media_type": attachment.media_type,
58
+ "relative_path": attachment.relative_path,
59
+ "size_bytes": attachment.size_bytes,
60
+ "error": None,
61
+ "sha256": attachment.sha256,
62
+ }
29
63
 
30
64
 
31
- def serialize_inbound(message: InboundMessage) -> dict[str, object]:
65
+ def serialize_message(message: Message) -> dict[str, object]:
32
66
  return {
33
67
  "seq": message.seq,
34
68
  "message_id": message.message_id,
69
+ "direction": message.direction.value,
35
70
  "session_id": message.session_id,
36
71
  "channel_session_id": message.channel_session_id,
37
72
  "channel": message.channel,
38
73
  "received_at_ms": message.received_at_ms,
74
+ "created_at_ms": message.created_at_ms,
39
75
  "provider_time_ms": message.provider_time_ms,
40
76
  "sender": (
41
77
  None
@@ -43,26 +79,33 @@ def serialize_inbound(message: InboundMessage) -> dict[str, object]:
43
79
  else {"id": message.sender.id, "name": message.sender.name}
44
80
  ),
45
81
  "sender_kind": message.sender_kind.value,
82
+ "system_message_kind": (
83
+ None
84
+ if message.system_message_kind is None
85
+ else message.system_message_kind.value
86
+ ),
87
+ "system_message_source_target": message.metadata.get(
88
+ "system_message_source_target"
89
+ ),
90
+ "system_message_source_message_id": message.metadata.get(
91
+ "system_message_source_message_id"
92
+ ),
46
93
  "message_type": message.message_type,
47
- "canonical_target": message.canonical_target,
94
+ "canonical_target": message.target,
48
95
  "target_kind": message.target_kind.value,
49
96
  "mentions_agent": message.mentions_agent,
50
97
  "notifies_runtime": message.notifies_runtime,
51
98
  "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
99
+ _serialize_attachment(attachment) for attachment in message.attachments
63
100
  ],
64
101
  "body": message.body,
65
102
  "reply_to_message_id": message.reply_to_message_id,
103
+ "delivery_state": (
104
+ message.delivery_state.value
105
+ if message.direction is MessageDirection.OUTBOUND
106
+ and message.delivery_state is not None
107
+ else None
108
+ ),
66
109
  }
67
110
 
68
111
 
@@ -404,10 +447,10 @@ class CommandDispatcher:
404
447
  "ok": True,
405
448
  "result": {
406
449
  "messages": [
407
- serialize_inbound(message) for message in result.messages
450
+ serialize_message(message) for message in result.messages
408
451
  ],
409
452
  "referenced_messages": [
410
- serialize_inbound(message)
453
+ serialize_message(message)
411
454
  for message in result.referenced_messages
412
455
  ],
413
456
  "snapshot_seq": result.snapshot_seq,
@@ -436,10 +479,10 @@ class CommandDispatcher:
436
479
  "ok": True,
437
480
  "result": {
438
481
  "messages": [
439
- serialize_inbound(message) for message in result.messages
482
+ serialize_message(message) for message in result.messages
440
483
  ],
441
484
  "referenced_messages": [
442
- serialize_inbound(message)
485
+ serialize_message(message)
443
486
  for message in result.referenced_messages
444
487
  ],
445
488
  "snapshot_seq": result.snapshot_seq,
@@ -465,22 +508,19 @@ class CommandDispatcher:
465
508
  "ok": True,
466
509
  "result": {"text": format_freshness_hold(result)},
467
510
  }
468
- if isinstance(result, MessageSendHandoffRequired):
469
- return {
470
- "ok": True,
471
- "result": {"text": format_cross_session_hold(result.target)},
472
- }
473
- if result.state is OutboundDeliveryState.SENT:
511
+ delivery_state = result.delivery_state
512
+ if delivery_state is None:
513
+ raise RuntimeError("outbound message has no delivery state")
514
+ if delivery_state is OutboundDeliveryState.SENT:
474
515
  text = (
475
- f"Message sent to {result.target}. "
476
- f"Message ID: {result.outbound_message_id}"
516
+ f"Message sent to {result.target}. Message ID: {result.message_id}"
477
517
  )
478
- elif result.state is OutboundDeliveryState.QUEUED:
518
+ elif delivery_state is OutboundDeliveryState.QUEUED:
479
519
  text = (
480
520
  f"Message queued to {result.target}. "
481
- f"Message ID: {result.outbound_message_id}"
521
+ f"Message ID: {result.message_id}"
482
522
  )
483
- elif result.state is OutboundDeliveryState.PARTIAL:
523
+ elif delivery_state is OutboundDeliveryState.PARTIAL:
484
524
  raise CommandDispatchError(
485
525
  "SEND_PARTIAL",
486
526
  result.error_message
@@ -490,13 +530,13 @@ class CommandDispatcher:
490
530
  "confirmed delivery first."
491
531
  ),
492
532
  )
493
- elif result.state is OutboundDeliveryState.UNKNOWN:
533
+ elif delivery_state is OutboundDeliveryState.UNKNOWN:
494
534
  raise CommandDispatchError(
495
535
  "SEND_UNKNOWN",
496
536
  result.error_message or "Message delivery outcome is unknown.",
497
537
  next_action="Reconcile channel delivery before retrying.",
498
538
  )
499
- elif result.state is OutboundDeliveryState.FAILED:
539
+ elif delivery_state is OutboundDeliveryState.FAILED:
500
540
  raise CommandDispatchError(
501
541
  "SEND_FAILED",
502
542
  result.error_message or "Message delivery failed.",
@@ -504,7 +544,7 @@ class CommandDispatcher:
504
544
  )
505
545
  else:
506
546
  raise AssertionError(
507
- f"message send returned unsupported state: {result.state.value}"
547
+ f"message send returned unsupported state: {delivery_state.value}"
508
548
  )
509
549
  return {
510
550
  "ok": True,
@@ -528,10 +568,11 @@ def format_message_time(timestamp_ms: int) -> str:
528
568
 
529
569
 
530
570
  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)
571
+ for field_name in ("provider_time_ms", "received_at_ms", "created_at_ms"):
572
+ timestamp = message.get(field_name)
573
+ if timestamp is not None:
574
+ return cast(int, timestamp)
575
+ raise ValueError("message has no display timestamp")
535
576
 
536
577
 
537
578
  def _message_header_fields(
@@ -546,7 +587,12 @@ def _message_header_fields(
546
587
  sender_mapping = cast(Mapping[str, object], sender_value)
547
588
  sender_id = cast(str | None, sender_mapping.get("id"))
548
589
  sender_name = cast(str | None, sender_mapping.get("name"))
549
- sender = f"@{sender_id}({sender_name})" if sender_name else f"@{sender_id}"
590
+ if sender_id is not None and sender_name is not None:
591
+ sender = f"@{sender_id}({sender_name})"
592
+ elif sender_name is not None:
593
+ sender = f"@{sender_name}"
594
+ elif sender_id is not None:
595
+ sender = f"@{sender_id}"
550
596
  return (
551
597
  target,
552
598
  message_id,
@@ -564,33 +610,62 @@ def _attachment_suffix(message: Mapping[str, object]) -> str:
564
610
  rendered: list[str] = []
565
611
  for attachment in attachments:
566
612
  name = cast(str, attachment["name"])
567
- attachment_id = cast(str, attachment["attachment_id"])
613
+ attachment_id = cast(str | None, attachment.get("attachment_id"))
568
614
  state = cast(str, attachment["state"])
615
+ identity = f"id:{attachment_id}, " if attachment_id is not None else ""
569
616
  if state == "ready":
570
617
  path = cast(str, attachment["relative_path"])
571
- rendered.append(f"{name} (id:{attachment_id}, path:{path})")
618
+ rendered.append(f"{name} ({identity}path:{path})")
572
619
  else:
573
620
  error = cast(str, attachment["error"])
574
- rendered.append(f"{name} (id:{attachment_id}, state:failed, error:{error})")
621
+ rendered.append(f"{name} ({identity}state:failed, error:{error})")
575
622
  label = "attachment" if len(rendered) == 1 else "attachments"
576
623
  return f" [{len(rendered)} {label}: {', '.join(rendered)}]"
577
624
 
578
625
 
579
626
  def format_check_message(message: Mapping[str, object]) -> str:
580
- target, message_id, timestamp, message_type, sender, body = _message_header_fields(
627
+ target, message_id, timestamp, sender_kind, sender, body = _message_header_fields(
581
628
  message
582
629
  )
583
- line = (
584
- f"[target={target} msg={message_id} time={timestamp} "
585
- f"type={message_type} mentioned={str(message['mentions_agent']).lower()}"
586
- )
630
+ line = f"[target={target} msg={message_id} time={timestamp} type={sender_kind}"
587
631
  reply_to_message_id = message["reply_to_message_id"]
588
632
  if reply_to_message_id is not None:
589
633
  line += f" reply_to={cast(str, reply_to_message_id)}"
590
634
  line += "] "
591
635
  if sender is not None:
592
- line += f"{sender} "
593
- return line + body + _attachment_suffix(message)
636
+ line += f"{sender}: "
637
+ rendered = line + body + _attachment_suffix(message)
638
+ if message.get("system_message_kind") == "reminder":
639
+ operation = (
640
+ "(to snooze/update/cancel: bcc reminder --help)"
641
+ if "\nNext iteration: " in body
642
+ else "(to snooze/cancel: bcc reminder --help)"
643
+ )
644
+ rendered += (
645
+ f"\n{operation}"
646
+ "\nRespond as appropriate. Complete all your work before stopping."
647
+ "\nReply in the channel or create/reply in a thread as appropriate; "
648
+ "use each message's `target` and `msg` fields to choose the exact target."
649
+ )
650
+ elif message.get("system_message_kind") == "handoff":
651
+ source_target = cast(str, message["system_message_source_target"])
652
+ source_message_id = cast(
653
+ str,
654
+ message["system_message_source_message_id"],
655
+ )
656
+ rendered += (
657
+ "\nTo understand why this message was sent, inspect the source context:"
658
+ "\n bcc message read --target "
659
+ f"{json.dumps(source_target)} --around {json.dumps(source_message_id)}"
660
+ "\nIf you have no objection to why the message was sent, do not announce "
661
+ "or explain the handoff, and do not repeat or respond to the referenced "
662
+ "message; it has already been delivered. Continue only work already in "
663
+ "progress in this conversation that is independent of that message; if "
664
+ "there is none, stop."
665
+ "\nMention the handoff only when its reason is unclear, conflicts with "
666
+ "the current conversation, or requires a decision."
667
+ )
668
+ return rendered
594
669
 
595
670
 
596
671
  def format_read_message(
@@ -599,30 +674,29 @@ def format_read_message(
599
674
  index: int,
600
675
  count: int,
601
676
  ) -> str:
602
- target, message_id, timestamp, message_type, sender, body = _message_header_fields(
677
+ target, message_id, timestamp, sender_kind, sender, body = _message_header_fields(
603
678
  message
604
679
  )
605
680
  fields = [
606
681
  f"seq={cast(int, message['seq'])}",
607
682
  f"msg={message_id}",
608
683
  f"time={timestamp}",
609
- f"type={message_type}",
684
+ f"type={sender_kind}",
610
685
  f"replyTarget={target}",
611
- f"mentioned={str(message['mentions_agent']).lower()}",
612
686
  ]
613
687
  reply_to_message_id = message["reply_to_message_id"]
614
688
  if reply_to_message_id is not None:
615
689
  fields.append(f"replyTo={cast(str, reply_to_message_id)}")
616
690
  line = f"[{index}/{count} {' '.join(fields)}] "
617
691
  if sender is not None:
618
- line += f"{sender} "
692
+ line += f"{sender}: "
619
693
  return line + body + _attachment_suffix(message)
620
694
 
621
695
 
622
696
  def format_freshness_hold(result: MessageSendFreshnessHold) -> str:
623
- messages = [serialize_inbound(message) for message in result.messages]
697
+ messages = [serialize_message(message) for message in result.messages]
624
698
  referenced_messages = [
625
- serialize_inbound(message) for message in result.referenced_messages
699
+ serialize_message(message) for message in result.referenced_messages
626
700
  ]
627
701
  shown = len(messages)
628
702
  total = result.newer_message_total
@@ -676,23 +750,3 @@ def format_freshness_hold(result: MessageSendFreshnessHold) -> str:
676
750
  )
677
751
  )
678
752
  return "\n".join(lines)
679
-
680
-
681
- def format_cross_session_hold(target: str) -> str:
682
- quoted_target = json.dumps(target)
683
- return "\n".join(
684
- (
685
- "Your message was not sent because the target belongs to another conversation.",
686
- "",
687
- (
688
- "To continue this work in the target conversation, send a "
689
- "self-contained handoff:"
690
- ),
691
- f" bcc handoff send --target {quoted_target} <<'BCCMSG'",
692
- " enough context to understand the background, goal, and next action",
693
- " BCCMSG",
694
- "This creates a handoff notice that wakes you in that conversation.",
695
- "",
696
- "You can also choose not to send anything.",
697
- )
698
- )