bazaar-compute-node 0.1.26__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 (137) hide show
  1. {bazaar_compute_node-0.1.26 → bazaar_compute_node-0.1.27}/PKG-INFO +1 -1
  2. {bazaar_compute_node-0.1.26 → bazaar_compute_node-0.1.27}/pyproject.toml +1 -1
  3. {bazaar_compute_node-0.1.26 → bazaar_compute_node-0.1.27}/pyproject.toml.orig +1 -1
  4. {bazaar_compute_node-0.1.26 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/app/agent.py +4 -26
  5. {bazaar_compute_node-0.1.26 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/app/application.py +10 -5
  6. {bazaar_compute_node-0.1.26 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/app/command.py +46 -34
  7. {bazaar_compute_node-0.1.26 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/app/resource_dispatch.py +3 -197
  8. {bazaar_compute_node-0.1.26 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/bcc.py +5 -216
  9. {bazaar_compute_node-0.1.26 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/contrib/lark/channel.py +2 -0
  10. {bazaar_compute_node-0.1.26 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/contrib/sqlite/codec.py +11 -65
  11. {bazaar_compute_node-0.1.26 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/contrib/sqlite/migrations/__init__.py +2 -0
  12. {bazaar_compute_node-0.1.26 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/contrib/sqlite/migrations/registry.py +8 -0
  13. bazaar_compute_node-0.1.27/src/bazaar_compute_node/contrib/sqlite/migrations/v19_reminder_system_messages.py +226 -0
  14. bazaar_compute_node-0.1.27/src/bazaar_compute_node/contrib/sqlite/migrations/v20_remove_freshness_persistence.py +162 -0
  15. bazaar_compute_node-0.1.27/src/bazaar_compute_node/contrib/sqlite/migrations/v21_remove_handoffs.py +14 -0
  16. {bazaar_compute_node-0.1.26 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/contrib/sqlite/reminder_codec.py +2 -19
  17. {bazaar_compute_node-0.1.26 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/contrib/sqlite/repository/base.py +19 -0
  18. {bazaar_compute_node-0.1.26 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/contrib/sqlite/repository/facade.py +0 -2
  19. {bazaar_compute_node-0.1.26 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/contrib/sqlite/repository/messages.py +122 -14
  20. {bazaar_compute_node-0.1.26 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/contrib/sqlite/repository/reminders.py +61 -251
  21. {bazaar_compute_node-0.1.26 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/contrib/sqlite/repository/sessions.py +4 -15
  22. {bazaar_compute_node-0.1.26 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/contrib/sqlite/storage.py +12 -25
  23. {bazaar_compute_node-0.1.26 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/contrib/telegram/channel.py +2 -0
  24. {bazaar_compute_node-0.1.26 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/core/command.py +19 -41
  25. {bazaar_compute_node-0.1.26 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/core/instruction.py +18 -53
  26. {bazaar_compute_node-0.1.26 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/core/models/__init__.py +4 -6
  27. {bazaar_compute_node-0.1.26 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/core/models/entities.py +31 -173
  28. {bazaar_compute_node-0.1.26 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/core/models/reminder.py +0 -76
  29. bazaar_compute_node-0.1.27/src/bazaar_compute_node/core/models/reminder_owner.py +28 -0
  30. {bazaar_compute_node-0.1.26 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/core/models/states.py +8 -18
  31. {bazaar_compute_node-0.1.26 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/core/orchestration/command.py +128 -78
  32. {bazaar_compute_node-0.1.26 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/core/orchestration/error_feedback.py +3 -1
  33. {bazaar_compute_node-0.1.26 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/core/orchestration/reminder.py +64 -35
  34. {bazaar_compute_node-0.1.26 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/core/orchestration/reminder_command.py +0 -24
  35. {bazaar_compute_node-0.1.26 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/core/orchestration/session.py +84 -182
  36. {bazaar_compute_node-0.1.26 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/core/orchestration/turn.py +62 -23
  37. {bazaar_compute_node-0.1.26 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/core/reminder.py +25 -37
  38. {bazaar_compute_node-0.1.26 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/core/storage.py +250 -315
  39. {bazaar_compute_node-0.1.26 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/i18n/english.py +0 -1
  40. {bazaar_compute_node-0.1.26 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/i18n/schinese.py +0 -1
  41. bazaar_compute_node-0.1.26/src/bazaar_compute_node/contrib/sqlite/handoff_codec.py +0 -24
  42. bazaar_compute_node-0.1.26/src/bazaar_compute_node/contrib/sqlite/repository/handoffs.py +0 -140
  43. bazaar_compute_node-0.1.26/src/bazaar_compute_node/core/handoff.py +0 -49
  44. bazaar_compute_node-0.1.26/src/bazaar_compute_node/core/models/handoff.py +0 -26
  45. bazaar_compute_node-0.1.26/src/bazaar_compute_node/core/models/reminder_owner.py +0 -59
  46. bazaar_compute_node-0.1.26/src/bazaar_compute_node/core/orchestration/handoff_command.py +0 -245
  47. {bazaar_compute_node-0.1.26 → bazaar_compute_node-0.1.27}/LICENSE +0 -0
  48. {bazaar_compute_node-0.1.26 → bazaar_compute_node-0.1.27}/README.md +0 -0
  49. {bazaar_compute_node-0.1.26 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/__init__.py +0 -0
  50. {bazaar_compute_node-0.1.26 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/app/__init__.py +0 -0
  51. {bazaar_compute_node-0.1.26 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/app/agent_management.py +0 -0
  52. {bazaar_compute_node-0.1.26 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/app/attachments.py +0 -0
  53. {bazaar_compute_node-0.1.26 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/app/config.py +0 -0
  54. {bazaar_compute_node-0.1.26 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/app/registry.py +0 -0
  55. {bazaar_compute_node-0.1.26 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/app/system_service.py +0 -0
  56. {bazaar_compute_node-0.1.26 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/app/transport.py +0 -0
  57. {bazaar_compute_node-0.1.26 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/app/windows_pipe.py +0 -0
  58. {bazaar_compute_node-0.1.26 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/app/wrapper.py +0 -0
  59. {bazaar_compute_node-0.1.26 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/cli.py +0 -0
  60. {bazaar_compute_node-0.1.26 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/contrib/__init__.py +0 -0
  61. {bazaar_compute_node-0.1.26 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/contrib/codex/__init__.py +0 -0
  62. {bazaar_compute_node-0.1.26 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/contrib/codex/approval.py +0 -0
  63. {bazaar_compute_node-0.1.26 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/contrib/codex/client.py +0 -0
  64. {bazaar_compute_node-0.1.26 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/contrib/codex/events.py +0 -0
  65. {bazaar_compute_node-0.1.26 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/contrib/codex/plugin.py +0 -0
  66. {bazaar_compute_node-0.1.26 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/contrib/codex/process.py +0 -0
  67. {bazaar_compute_node-0.1.26 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/contrib/codex/protocol.py +0 -0
  68. {bazaar_compute_node-0.1.26 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/contrib/codex/runtime.py +0 -0
  69. {bazaar_compute_node-0.1.26 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/contrib/lark/__init__.py +0 -0
  70. {bazaar_compute_node-0.1.26 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/contrib/lark/api.py +0 -0
  71. {bazaar_compute_node-0.1.26 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/contrib/lark/approval.py +0 -0
  72. {bazaar_compute_node-0.1.26 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/contrib/lark/attachments.py +0 -0
  73. {bazaar_compute_node-0.1.26 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/contrib/lark/frame.py +0 -0
  74. {bazaar_compute_node-0.1.26 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/contrib/lark/identity.py +0 -0
  75. {bazaar_compute_node-0.1.26 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/contrib/lark/outbound.py +0 -0
  76. {bazaar_compute_node-0.1.26 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/contrib/lark/plugin.py +0 -0
  77. {bazaar_compute_node-0.1.26 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/contrib/lark/transport.py +0 -0
  78. {bazaar_compute_node-0.1.26 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/contrib/logging/__init__.py +0 -0
  79. {bazaar_compute_node-0.1.26 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/contrib/logging/audit.py +0 -0
  80. {bazaar_compute_node-0.1.26 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/contrib/logging/plugin.py +0 -0
  81. {bazaar_compute_node-0.1.26 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/contrib/sqlite/__init__.py +0 -0
  82. {bazaar_compute_node-0.1.26 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/contrib/sqlite/database.py +0 -0
  83. {bazaar_compute_node-0.1.26 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/contrib/sqlite/executor.py +0 -0
  84. {bazaar_compute_node-0.1.26 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/contrib/sqlite/migrations/model.py +0 -0
  85. {bazaar_compute_node-0.1.26 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/contrib/sqlite/migrations/v01_initial_node_schema.py +0 -0
  86. {bazaar_compute_node-0.1.26 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/contrib/sqlite/migrations/v02_session_mapping_indexes.py +0 -0
  87. {bazaar_compute_node-0.1.26 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/contrib/sqlite/migrations/v03_message_log_indexes.py +0 -0
  88. {bazaar_compute_node-0.1.26 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/contrib/sqlite/migrations/v04_runtime_attempt_facts.py +0 -0
  89. {bazaar_compute_node-0.1.26 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/contrib/sqlite/migrations/v05_inbound_message_references.py +0 -0
  90. {bazaar_compute_node-0.1.26 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/contrib/sqlite/migrations/v06_inbound_message_reference_integrity.py +0 -0
  91. {bazaar_compute_node-0.1.26 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/contrib/sqlite/migrations/v07_inbound_provider_identity.py +0 -0
  92. {bazaar_compute_node-0.1.26 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/contrib/sqlite/migrations/v08_transient_stream_events.py +0 -0
  93. {bazaar_compute_node-0.1.26 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/contrib/sqlite/migrations/v09_remove_runtime_events.py +0 -0
  94. {bazaar_compute_node-0.1.26 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/contrib/sqlite/migrations/v10_add_outbound_attachments.py +0 -0
  95. {bazaar_compute_node-0.1.26 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/contrib/sqlite/migrations/v11_remove_runtime_session_mapping.py +0 -0
  96. {bazaar_compute_node-0.1.26 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/contrib/sqlite/migrations/v12_add_reminders.py +0 -0
  97. {bazaar_compute_node-0.1.26 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/contrib/sqlite/migrations/v13_add_agent_ownership.py +0 -0
  98. {bazaar_compute_node-0.1.26 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/contrib/sqlite/migrations/v14_add_inbox_discovery_indexes.py +0 -0
  99. {bazaar_compute_node-0.1.26 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/contrib/sqlite/migrations/v15_add_handoffs.py +0 -0
  100. {bazaar_compute_node-0.1.26 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/contrib/sqlite/migrations/v16_remove_outbound_drafts.py +0 -0
  101. {bazaar_compute_node-0.1.26 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/contrib/sqlite/migrations/v17_remove_agent_identity_triggers.py +0 -0
  102. {bazaar_compute_node-0.1.26 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/contrib/sqlite/migrations/v18_unify_messages.py +0 -0
  103. {bazaar_compute_node-0.1.26 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/contrib/sqlite/plugin.py +0 -0
  104. {bazaar_compute_node-0.1.26 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/contrib/sqlite/repository/__init__.py +0 -0
  105. {bazaar_compute_node-0.1.26 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/contrib/telegram/__init__.py +0 -0
  106. {bazaar_compute_node-0.1.26 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/contrib/telegram/api.py +0 -0
  107. {bazaar_compute_node-0.1.26 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/contrib/telegram/approval.py +0 -0
  108. {bazaar_compute_node-0.1.26 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/contrib/telegram/attachments.py +0 -0
  109. {bazaar_compute_node-0.1.26 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/contrib/telegram/identity.py +0 -0
  110. {bazaar_compute_node-0.1.26 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/contrib/telegram/markdown.py +0 -0
  111. {bazaar_compute_node-0.1.26 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/contrib/telegram/outbound.py +0 -0
  112. {bazaar_compute_node-0.1.26 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/contrib/telegram/plugin.py +0 -0
  113. {bazaar_compute_node-0.1.26 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/contrib/wecom/__init__.py +0 -0
  114. {bazaar_compute_node-0.1.26 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/contrib/wecom/channel.py +0 -0
  115. {bazaar_compute_node-0.1.26 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/contrib/wecom/markdown.py +0 -0
  116. {bazaar_compute_node-0.1.26 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/contrib/wecom/outbound.py +0 -0
  117. {bazaar_compute_node-0.1.26 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/contrib/wecom/plugin.py +0 -0
  118. {bazaar_compute_node-0.1.26 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/core/__init__.py +0 -0
  119. {bazaar_compute_node-0.1.26 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/core/approval.py +0 -0
  120. {bazaar_compute_node-0.1.26 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/core/audit.py +0 -0
  121. {bazaar_compute_node-0.1.26 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/core/channel.py +0 -0
  122. {bazaar_compute_node-0.1.26 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/core/client.py +0 -0
  123. {bazaar_compute_node-0.1.26 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/core/concurrency.py +0 -0
  124. {bazaar_compute_node-0.1.26 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/core/correlation.py +0 -0
  125. {bazaar_compute_node-0.1.26 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/core/inbox.py +0 -0
  126. {bazaar_compute_node-0.1.26 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/core/lifecycle.py +0 -0
  127. {bazaar_compute_node-0.1.26 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/core/observability.py +0 -0
  128. {bazaar_compute_node-0.1.26 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/core/orchestration/__init__.py +0 -0
  129. {bazaar_compute_node-0.1.26 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/core/orchestration/delivery.py +0 -0
  130. {bazaar_compute_node-0.1.26 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/core/orchestration/services.py +0 -0
  131. {bazaar_compute_node-0.1.26 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/core/outcomes.py +0 -0
  132. {bazaar_compute_node-0.1.26 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/core/paths.py +0 -0
  133. {bazaar_compute_node-0.1.26 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/core/reminder_schedule.py +0 -0
  134. {bazaar_compute_node-0.1.26 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/core/runtime.py +0 -0
  135. {bazaar_compute_node-0.1.26 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/core/timerwheel.py +0 -0
  136. {bazaar_compute_node-0.1.26 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/i18n/__init__.py +0 -0
  137. {bazaar_compute_node-0.1.26 → 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.26
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.26"
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.26"
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
@@ -281,15 +264,10 @@ class AgentApplication:
281
264
  return False
282
265
  return await self.storage.get_bcn_session(session_id) is not None
283
266
 
284
- async def publish_reminder_wake(self, session_id: str) -> None:
285
- if not self._started:
286
- raise RuntimeError("Agent application is not started")
287
- await self.orchestrator.publish_reminder_wake(session_id)
288
-
289
- async def publish_handoff_wake(self, session_id: str) -> None:
267
+ async def publish_inbox_wake(self, message: Message[InboundAttachment]) -> None:
290
268
  if not self._started:
291
269
  raise RuntimeError("Agent application is not started")
292
- await self.orchestrator.publish_handoff_wake(session_id)
270
+ await self.orchestrator.publish_inbox_wake(message)
293
271
 
294
272
  def health_record(self) -> dict[str, object]:
295
273
  return {
@@ -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,7 +21,6 @@ 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
@@ -80,6 +79,17 @@ def serialize_message(message: Message) -> dict[str, object]:
80
79
  else {"id": message.sender.id, "name": message.sender.name}
81
80
  ),
82
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
+ ),
83
93
  "message_type": message.message_type,
84
94
  "canonical_target": message.target,
85
95
  "target_kind": message.target_kind.value,
@@ -498,11 +508,6 @@ class CommandDispatcher:
498
508
  "ok": True,
499
509
  "result": {"text": format_freshness_hold(result)},
500
510
  }
501
- if isinstance(result, MessageSendHandoffRequired):
502
- return {
503
- "ok": True,
504
- "result": {"text": format_cross_session_hold(result.target)},
505
- }
506
511
  delivery_state = result.delivery_state
507
512
  if delivery_state is None:
508
513
  raise RuntimeError("outbound message has no delivery state")
@@ -622,17 +627,45 @@ def format_check_message(message: Mapping[str, object]) -> str:
622
627
  target, message_id, timestamp, sender_kind, sender, body = _message_header_fields(
623
628
  message
624
629
  )
625
- line = (
626
- f"[target={target} msg={message_id} time={timestamp} "
627
- f"type={sender_kind} mentioned={str(message['mentions_agent']).lower()}"
628
- )
630
+ line = f"[target={target} msg={message_id} time={timestamp} type={sender_kind}"
629
631
  reply_to_message_id = message["reply_to_message_id"]
630
632
  if reply_to_message_id is not None:
631
633
  line += f" reply_to={cast(str, reply_to_message_id)}"
632
634
  line += "] "
633
635
  if sender is not None:
634
- line += f"{sender} "
635
- 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
636
669
 
637
670
 
638
671
  def format_read_message(
@@ -650,14 +683,13 @@ def format_read_message(
650
683
  f"time={timestamp}",
651
684
  f"type={sender_kind}",
652
685
  f"replyTarget={target}",
653
- f"mentioned={str(message['mentions_agent']).lower()}",
654
686
  ]
655
687
  reply_to_message_id = message["reply_to_message_id"]
656
688
  if reply_to_message_id is not None:
657
689
  fields.append(f"replyTo={cast(str, reply_to_message_id)}")
658
690
  line = f"[{index}/{count} {' '.join(fields)}] "
659
691
  if sender is not None:
660
- line += f"{sender} "
692
+ line += f"{sender}: "
661
693
  return line + body + _attachment_suffix(message)
662
694
 
663
695
 
@@ -718,23 +750,3 @@ def format_freshness_hold(result: MessageSendFreshnessHold) -> str:
718
750
  )
719
751
  )
720
752
  return "\n".join(lines)
721
-
722
-
723
- def format_cross_session_hold(target: str) -> str:
724
- quoted_target = json.dumps(target)
725
- return "\n".join(
726
- (
727
- "Your message was not sent because the target belongs to another conversation.",
728
- "",
729
- (
730
- "To continue this work in the target conversation, send a "
731
- "self-contained handoff:"
732
- ),
733
- f" bcc handoff send --target {quoted_target} <<'BCCMSG'",
734
- " enough context to understand the background, goal, and next action",
735
- " BCCMSG",
736
- "This creates a handoff notice that wakes you in that conversation.",
737
- "",
738
- "You can also choose not to send anything.",
739
- )
740
- )
@@ -4,22 +4,18 @@ from collections.abc import Mapping
4
4
  from time import time_ns
5
5
  from typing import Annotated, Literal, cast
6
6
 
7
- from pydantic import Field, StrictBool, StrictInt, StrictStr, field_validator
7
+ from pydantic import Field, StrictBool, StrictInt, StrictStr
8
8
 
9
- from ..core.command import ICommandService, IHandoffService, IReminderService
10
- from ..core.handoff import HandoffCheckRequest, HandoffSendRequest
9
+ from ..core.command import ICommandService, IReminderService
11
10
  from ..core.lifecycle import TimeoutBudget
12
- from ..core.models import Handoff, Reminder, ReminderOccurrence, ReminderState
13
- from ..core.orchestration.handoff_command import HandoffCommandFailure
11
+ from ..core.models import Reminder, ReminderState
14
12
  from ..core.orchestration.reminder_command import ReminderCommandFailure
15
13
  from ..core.reminder import (
16
14
  ReminderCancelRequest,
17
- ReminderCheckRequest,
18
15
  ReminderListRequest,
19
16
  ReminderScheduleRequest,
20
17
  ReminderSnoozeRequest,
21
18
  ReminderUpdateRequest,
22
- canonical_id_reference,
23
19
  )
24
20
  from .command import CommandDispatcher as _MessageCommandDispatcher
25
21
  from .command import (
@@ -50,45 +46,8 @@ def serialize_reminder(reminder: Reminder) -> dict[str, object]:
50
46
  }
51
47
 
52
48
 
53
- def serialize_reminder_occurrence(
54
- occurrence: ReminderOccurrence,
55
- ) -> dict[str, object]:
56
- return {
57
- "occurrence_id": occurrence.occurrence_id,
58
- "reminder_id": occurrence.reminder_id,
59
- "owner_session_id": occurrence.owner_session_id,
60
- "occurrence_no": occurrence.occurrence_no,
61
- "anchor_message_id": occurrence.anchor_message_id,
62
- "scheduled_for_ms": occurrence.scheduled_for_ms,
63
- "fired_at_ms": occurrence.fired_at_ms,
64
- "next_fire_at_ms": occurrence.next_fire_at_ms,
65
- "overdue": occurrence.overdue,
66
- "read_at_ms": occurrence.read_at_ms,
67
- "created_at_ms": occurrence.created_at_ms,
68
- }
69
-
70
-
71
- def serialize_handoff(handoff: Handoff) -> dict[str, object]:
72
- return {
73
- "handoff_id": handoff.handoff_id,
74
- "command_id": handoff.command_id,
75
- "source_session_id": handoff.source_session_id,
76
- "target_session_id": handoff.target_session_id,
77
- "source_message_id": handoff.source_message_id,
78
- "body": handoff.body,
79
- "created_at_ms": handoff.created_at_ms,
80
- "read_at_ms": handoff.read_at_ms,
81
- }
82
-
83
-
84
49
  NonEmptyText = Annotated[StrictStr, Field(min_length=1)]
85
50
  PositiveInt = Annotated[StrictInt, Field(gt=0)]
86
- NonNegativeInt = Annotated[StrictInt, Field(ge=0)]
87
-
88
-
89
- class _ReminderCheckRequest(_CommandRequest):
90
- resource: Literal["reminder"]
91
- command: Literal["check"]
92
51
 
93
52
 
94
53
  class _ReminderScheduleRequest(_CommandRequest):
@@ -132,39 +91,9 @@ class _ReminderCancelRequest(_CommandRequest):
132
91
  reminder_id: NonEmptyText
133
92
 
134
93
 
135
- class _HandoffSendRequest(_CommandRequest):
136
- resource: Literal["handoff"]
137
- command: Literal["send"]
138
- target: NonEmptyText
139
- body: StrictStr
140
- command_id: NonEmptyText
141
- source_message_id: NonEmptyText | None = None
142
- created_at_ms: NonNegativeInt = Field(
143
- default_factory=lambda: time_ns() // 1_000_000
144
- )
145
-
146
- @field_validator("body")
147
- @classmethod
148
- def _require_body(cls, value: str) -> str:
149
- if not value.strip():
150
- raise ValueError("handoff body must contain non-whitespace text")
151
- return value
152
-
153
- @field_validator("source_message_id")
154
- @classmethod
155
- def _require_source_message_id(cls, value: str | None) -> str | None:
156
- return None if value is None else canonical_id_reference(value)
157
-
158
-
159
- class _HandoffCheckRequest(_CommandRequest):
160
- resource: Literal["handoff"]
161
- command: Literal["check"]
162
-
163
-
164
94
  _REMINDER_REQUESTS: dict[
165
95
  str, tuple[type[_CommandRequest], Mapping[str, tuple[str, str]]]
166
96
  ] = {
167
- "check": (_ReminderCheckRequest, {}),
168
97
  "schedule": (
169
98
  _ReminderScheduleRequest,
170
99
  {
@@ -246,37 +175,6 @@ _REMINDER_REQUESTS: dict[
246
175
  ),
247
176
  }
248
177
 
249
- _HANDOFF_REQUESTS: dict[
250
- str, tuple[type[_CommandRequest], Mapping[str, tuple[str, str]]]
251
- ] = {
252
- "send": (
253
- _HandoffSendRequest,
254
- {
255
- "target": (
256
- "HANDOFF_TARGET_REQUIRED",
257
- "Handoff --target is required.",
258
- ),
259
- "body": (
260
- "HANDOFF_BODY_REQUIRED",
261
- "Handoff body must be non-empty text read from stdin.",
262
- ),
263
- "command_id": (
264
- "COMMAND_ID_REQUIRED",
265
- "command_id must be a non-empty string",
266
- ),
267
- "source_message_id": (
268
- "HANDOFF_SOURCE_INVALID",
269
- "Handoff --message-id must be a full UUID.",
270
- ),
271
- "created_at_ms": (
272
- "INVALID_CREATED_AT",
273
- "created_at_ms must be non-negative",
274
- ),
275
- },
276
- ),
277
- "check": (_HandoffCheckRequest, {}),
278
- }
279
-
280
178
 
281
179
  class CommandDispatcher(_MessageCommandDispatcher):
282
180
  """Resource-aware dispatcher for all local Agent commands."""
@@ -286,7 +184,6 @@ class CommandDispatcher(_MessageCommandDispatcher):
286
184
  service: ICommandService,
287
185
  *,
288
186
  reminder_service: IReminderService,
289
- handoff_service: IHandoffService,
290
187
  timeout_budget: TimeoutBudget,
291
188
  control_handler: ControlHandler | None = None,
292
189
  session_binding_validator: SessionBindingValidator | None = None,
@@ -298,7 +195,6 @@ class CommandDispatcher(_MessageCommandDispatcher):
298
195
  session_binding_validator=session_binding_validator,
299
196
  )
300
197
  self._reminder_service = reminder_service
301
- self._handoff_service = handoff_service
302
198
 
303
199
  async def _dispatch_command(
304
200
  self,
@@ -333,8 +229,6 @@ class CommandDispatcher(_MessageCommandDispatcher):
333
229
  return await super()._dispatch_command(raw_request)
334
230
  if resource == "inbox":
335
231
  return await super()._dispatch_command(raw_request)
336
- if resource == "handoff":
337
- return await self._dispatch_handoff(raw_request, command)
338
232
  if resource != "reminder":
339
233
  raise CommandDispatchError(
340
234
  "UNKNOWN_RESOURCE",
@@ -366,71 +260,6 @@ class CommandDispatcher(_MessageCommandDispatcher):
366
260
  next_action=error.next_action,
367
261
  ) from error
368
262
 
369
- async def _dispatch_handoff(
370
- self,
371
- raw_request: Mapping[str, object],
372
- command: str,
373
- ) -> Mapping[str, object]:
374
- request_spec = _HANDOFF_REQUESTS.get(command)
375
- if request_spec is None:
376
- raise CommandDispatchError(
377
- "UNKNOWN_COMMAND",
378
- f"unsupported handoff command: {command}",
379
- )
380
- request_model, request_errors = request_spec
381
- request = _parse_command_request(
382
- raw_request,
383
- request_model,
384
- errors=request_errors,
385
- )
386
- session_id = request.session_id
387
- if self._session_binding_validator is not None:
388
- await self._session_binding_validator(session_id, raw_request)
389
- try:
390
- if command == "send":
391
- values = cast(_HandoffSendRequest, request)
392
- result = await self._handoff_service.send(
393
- session_id,
394
- HandoffSendRequest(
395
- target=values.target,
396
- body=values.body,
397
- command_id=values.command_id,
398
- created_at_ms=values.created_at_ms,
399
- source_message_id=values.source_message_id,
400
- ),
401
- )
402
- return {
403
- "ok": True,
404
- "result": {
405
- "handoff": serialize_handoff(result.handoff),
406
- "target": result.target,
407
- },
408
- }
409
-
410
- result = await self._handoff_service.check(
411
- session_id,
412
- HandoffCheckRequest(),
413
- )
414
- return {
415
- "ok": True,
416
- "result": {
417
- "items": [
418
- {
419
- "handoff": serialize_handoff(item.handoff),
420
- "source_target": item.source_target,
421
- }
422
- for item in result.items
423
- ],
424
- "has_more": result.has_more,
425
- },
426
- }
427
- except HandoffCommandFailure as error:
428
- raise CommandDispatchError(
429
- error.code,
430
- error.message,
431
- next_action=error.next_action,
432
- ) from error
433
-
434
263
  async def _dispatch_reminder(
435
264
  self,
436
265
  session_id: str,
@@ -486,28 +315,6 @@ class CommandDispatcher(_MessageCommandDispatcher):
486
315
  "result": {"reminder": serialize_reminder(result.reminder)},
487
316
  }
488
317
 
489
- if command == "check":
490
- result = await self._reminder_service.check(
491
- session_id,
492
- ReminderCheckRequest(),
493
- )
494
- return {
495
- "ok": True,
496
- "result": {
497
- "items": [
498
- {
499
- "occurrence": serialize_reminder_occurrence(
500
- item.occurrence
501
- ),
502
- "title": item.title,
503
- "canonical_target": item.canonical_target,
504
- }
505
- for item in result.items
506
- ],
507
- "has_more": result.has_more,
508
- },
509
- }
510
-
511
318
  if command == "list":
512
319
  request_values = cast(_ReminderListRequest, parsed_request)
513
320
  all_statuses = request_values.all
@@ -626,5 +433,4 @@ class CommandDispatcher(_MessageCommandDispatcher):
626
433
  __all__ = [
627
434
  "CommandDispatcher",
628
435
  "serialize_reminder",
629
- "serialize_reminder_occurrence",
630
436
  ]