bazaar-compute-node 0.2.0rc1__tar.gz → 0.2.0rc3__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 (201) hide show
  1. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/PKG-INFO +5 -2
  2. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/README.md +4 -1
  3. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/pyproject.toml +1 -1
  4. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/pyproject.toml.orig +1 -1
  5. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/app/agent.py +9 -1
  6. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/contrib/lark/api.py +33 -6
  7. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/contrib/lark/approval.py +14 -4
  8. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/contrib/lark/channel.py +26 -1
  9. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/contrib/lark/outbound.py +7 -3
  10. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/contrib/sqlite/repository/messages.py +56 -2
  11. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/contrib/sqlite/storage.py +1 -0
  12. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/contrib/telegram/api.py +5 -2
  13. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/contrib/telegram/approval.py +9 -3
  14. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/contrib/telegram/channel.py +68 -1
  15. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/contrib/telegram/identity.py +19 -3
  16. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/contrib/telegram/outbound.py +2 -1
  17. bazaar_compute_node-0.2.0rc3/src/bazaar_compute_node/contrib/telegram/plugin.py +43 -0
  18. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/contrib/wecom/channel.py +14 -0
  19. bazaar_compute_node-0.2.0rc3/src/bazaar_compute_node/core/audit.py +153 -0
  20. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/core/channel.py +46 -0
  21. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/core/orchestration/command.py +83 -5
  22. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/core/orchestration/delivery.py +5 -1
  23. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/core/orchestration/error_feedback.py +1 -2
  24. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/core/orchestration/orchestrator.py +5 -9
  25. bazaar_compute_node-0.2.0rc3/src/bazaar_compute_node/core/orchestration/services.py +30 -0
  26. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/core/orchestration/turn.py +1 -2
  27. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/core/storage.py +21 -8
  28. bazaar_compute_node-0.2.0rc1/src/bazaar_compute_node/contrib/telegram/plugin.py +0 -23
  29. bazaar_compute_node-0.2.0rc1/src/bazaar_compute_node/core/audit.py +0 -70
  30. bazaar_compute_node-0.2.0rc1/src/bazaar_compute_node/core/orchestration/services.py +0 -118
  31. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/LICENSE +0 -0
  32. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/__init__.py +0 -0
  33. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/app/__init__.py +0 -0
  34. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/app/agent_management.py +0 -0
  35. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/app/application.py +0 -0
  36. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/app/attachments.py +0 -0
  37. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/app/command.py +0 -0
  38. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/app/config.py +0 -0
  39. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/app/registry.py +0 -0
  40. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/app/resource_dispatch.py +0 -0
  41. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/app/system_service.py +0 -0
  42. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/app/transport.py +0 -0
  43. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/app/upgrade.py +0 -0
  44. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/app/usage.py +0 -0
  45. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/app/version_check.py +0 -0
  46. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/app/windows_pipe.py +0 -0
  47. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/app/wrapper.py +0 -0
  48. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/bcc.py +0 -0
  49. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/cli.py +0 -0
  50. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/cmd/__init__.py +0 -0
  51. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/cmd/bcc/__init__.py +0 -0
  52. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/cmd/bcc/_client.py +0 -0
  53. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/cmd/bcc/_format.py +0 -0
  54. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/cmd/bcc/inbox.py +0 -0
  55. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/cmd/bcc/message.py +0 -0
  56. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/cmd/bcc/node.py +0 -0
  57. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/cmd/bcc/reminder.py +0 -0
  58. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/cmd/bcc/thread.py +0 -0
  59. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/cmd/bcn/__init__.py +0 -0
  60. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/cmd/bcn/_options.py +0 -0
  61. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/cmd/bcn/_runner.py +0 -0
  62. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/cmd/bcn/agent.py +0 -0
  63. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/cmd/bcn/node.py +0 -0
  64. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/cmd/bcn/service.py +0 -0
  65. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/contrib/__init__.py +0 -0
  66. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/contrib/claude/__init__.py +0 -0
  67. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/contrib/claude/approval.py +0 -0
  68. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/contrib/claude/client.py +0 -0
  69. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/contrib/claude/events.py +0 -0
  70. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/contrib/claude/plugin.py +0 -0
  71. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/contrib/claude/process.py +0 -0
  72. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/contrib/claude/protocol.py +0 -0
  73. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/contrib/claude/runtime.py +0 -0
  74. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/contrib/codex/__init__.py +0 -0
  75. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/contrib/codex/approval.py +0 -0
  76. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/contrib/codex/client.py +0 -0
  77. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/contrib/codex/events.py +0 -0
  78. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/contrib/codex/plugin.py +0 -0
  79. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/contrib/codex/process.py +0 -0
  80. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/contrib/codex/protocol.py +0 -0
  81. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/contrib/codex/runtime.py +0 -0
  82. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/contrib/lark/__init__.py +0 -0
  83. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/contrib/lark/attachments.py +0 -0
  84. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/contrib/lark/frame.py +0 -0
  85. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/contrib/lark/identity.py +0 -0
  86. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/contrib/lark/plugin.py +0 -0
  87. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/contrib/lark/transport.py +0 -0
  88. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/contrib/logging/__init__.py +0 -0
  89. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/contrib/logging/audit.py +0 -0
  90. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/contrib/logging/plugin.py +0 -0
  91. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/contrib/sqlite/__init__.py +0 -0
  92. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/contrib/sqlite/codec.py +0 -0
  93. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/contrib/sqlite/database.py +0 -0
  94. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/contrib/sqlite/executor.py +0 -0
  95. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/contrib/sqlite/migrations/__init__.py +0 -0
  96. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/contrib/sqlite/migrations/model.py +0 -0
  97. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/contrib/sqlite/migrations/registry.py +0 -0
  98. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/contrib/sqlite/migrations/v01_initial_node_schema.py +0 -0
  99. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/contrib/sqlite/migrations/v02_session_mapping_indexes.py +0 -0
  100. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/contrib/sqlite/migrations/v03_message_log_indexes.py +0 -0
  101. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/contrib/sqlite/migrations/v04_runtime_attempt_facts.py +0 -0
  102. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/contrib/sqlite/migrations/v05_inbound_message_references.py +0 -0
  103. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/contrib/sqlite/migrations/v06_inbound_message_reference_integrity.py +0 -0
  104. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/contrib/sqlite/migrations/v07_inbound_provider_identity.py +0 -0
  105. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/contrib/sqlite/migrations/v08_transient_stream_events.py +0 -0
  106. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/contrib/sqlite/migrations/v09_remove_runtime_events.py +0 -0
  107. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/contrib/sqlite/migrations/v10_add_outbound_attachments.py +0 -0
  108. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/contrib/sqlite/migrations/v11_remove_runtime_session_mapping.py +0 -0
  109. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/contrib/sqlite/migrations/v12_add_reminders.py +0 -0
  110. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/contrib/sqlite/migrations/v13_add_agent_ownership.py +0 -0
  111. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/contrib/sqlite/migrations/v14_add_inbox_discovery_indexes.py +0 -0
  112. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/contrib/sqlite/migrations/v15_add_handoffs.py +0 -0
  113. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/contrib/sqlite/migrations/v16_remove_outbound_drafts.py +0 -0
  114. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/contrib/sqlite/migrations/v17_remove_agent_identity_triggers.py +0 -0
  115. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/contrib/sqlite/migrations/v18_unify_messages.py +0 -0
  116. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/contrib/sqlite/migrations/v19_reminder_system_messages.py +0 -0
  117. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/contrib/sqlite/migrations/v20_remove_freshness_persistence.py +0 -0
  118. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/contrib/sqlite/migrations/v21_remove_handoffs.py +0 -0
  119. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/contrib/sqlite/migrations/v22_readable_targets.py +0 -0
  120. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/contrib/sqlite/migrations/v23_persist_sender_identity.py +0 -0
  121. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/contrib/sqlite/migrations/v24_persist_sender_display_name.py +0 -0
  122. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/contrib/sqlite/migrations/v25_rename_sessions_to_threads.py +0 -0
  123. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/contrib/sqlite/migrations/v26_remove_handoff_messages.py +0 -0
  124. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/contrib/sqlite/plugin.py +0 -0
  125. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/contrib/sqlite/reminder_codec.py +0 -0
  126. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/contrib/sqlite/repository/__init__.py +0 -0
  127. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/contrib/sqlite/repository/base.py +0 -0
  128. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/contrib/sqlite/repository/facade.py +0 -0
  129. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/contrib/sqlite/repository/reminders.py +0 -0
  130. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/contrib/sqlite/repository/sessions.py +0 -0
  131. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/contrib/telegram/__init__.py +0 -0
  132. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/contrib/telegram/attachments.py +0 -0
  133. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/contrib/telegram/markdown.py +0 -0
  134. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/contrib/wecom/__init__.py +0 -0
  135. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/contrib/wecom/outbound.py +0 -0
  136. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/contrib/wecom/plugin.py +0 -0
  137. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/core/__init__.py +0 -0
  138. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/core/actor.py +0 -0
  139. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/core/agent.py +0 -0
  140. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/core/approval.py +0 -0
  141. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/core/client.py +0 -0
  142. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/core/command.py +0 -0
  143. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/core/concurrency.py +0 -0
  144. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/core/correlation.py +0 -0
  145. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/core/inbox.py +0 -0
  146. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/core/instruction.py +0 -0
  147. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/core/lifecycle.py +0 -0
  148. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/core/models/__init__.py +0 -0
  149. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/core/models/entities.py +0 -0
  150. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/core/models/events.py +0 -0
  151. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/core/models/reminder.py +0 -0
  152. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/core/models/reminder_owner.py +0 -0
  153. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/core/models/states.py +0 -0
  154. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/core/observability.py +0 -0
  155. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/core/orchestration/__init__.py +0 -0
  156. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/core/orchestration/reminder.py +0 -0
  157. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/core/orchestration/reminder_command.py +0 -0
  158. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/core/orchestration/upgrade_notice.py +0 -0
  159. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/core/outcomes.py +0 -0
  160. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/core/paths.py +0 -0
  161. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/core/reminder.py +0 -0
  162. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/core/reminder_schedule.py +0 -0
  163. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/core/restart.py +0 -0
  164. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/core/runtime.py +0 -0
  165. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/core/timerwheel.py +0 -0
  166. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/core/utils/__init__.py +0 -0
  167. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/core/utils/clock.py +0 -0
  168. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/core/utils/line_reader.py +0 -0
  169. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/core/utils/markdown.py +0 -0
  170. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/core/utils/sanitization.py +0 -0
  171. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/core/utils/text.py +0 -0
  172. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/i18n/__init__.py +0 -0
  173. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/i18n/catalog.py +0 -0
  174. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/rendering.py +0 -0
  175. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/resources/approval_no_person.tpl +0 -0
  176. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/resources/bcc/check.tpl +0 -0
  177. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/resources/bcc/error.tpl +0 -0
  178. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/resources/bcc/inbox_check.tpl +0 -0
  179. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/resources/bcc/read.tpl +0 -0
  180. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/resources/bcc/reminder_list.tpl +0 -0
  181. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/resources/bcc/reminder_mutation.tpl +0 -0
  182. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/resources/bcc/reminder_schedule.tpl +0 -0
  183. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/resources/bcc/unfollow.tpl +0 -0
  184. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/resources/bcc/upgrade.tpl +0 -0
  185. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/resources/bcc/version.tpl +0 -0
  186. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/resources/command/attachment_suffix.tpl +0 -0
  187. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/resources/command/check_message.tpl +0 -0
  188. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/resources/command/freshness_hold.tpl +0 -0
  189. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/resources/command/read_message.tpl +0 -0
  190. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/resources/command/send_result.tpl +0 -0
  191. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/resources/command/sender.tpl +0 -0
  192. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/resources/developer_instructions.md +0 -0
  193. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/resources/inbox_notice.tpl +0 -0
  194. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/resources/locales/en.toml +0 -0
  195. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/resources/locales/zh-CN.toml +0 -0
  196. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/resources/system_service/launchd.plist +0 -0
  197. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/resources/system_service/launchd.sh +0 -0
  198. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/resources/system_service/systemd.service +0 -0
  199. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/resources/system_service/windows.ps1 +0 -0
  200. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/resources/system_service/windows.vbs +0 -0
  201. {bazaar_compute_node-0.2.0rc1 → bazaar_compute_node-0.2.0rc3}/src/bazaar_compute_node/resources/system_service/windows.xml +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: bazaar-compute-node
3
- Version: 0.2.0rc1
3
+ Version: 0.2.0rc3
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
@@ -51,11 +51,14 @@ bcn --version
51
51
  uv tool upgrade bazaar-compute-node
52
52
 
53
53
  # Add an Agent / 添加 Agent
54
+ # allowed_sender_ids holds the Telegram user ids allowed to open a private chat
55
+ # allowed_sender_ids 是允许与其私聊的 Telegram 用户 id
54
56
  bcn agent add \
55
57
  --name Tifa \
56
58
  --channel telegram \
57
59
  --runtime codex \
58
- --set channel.token_env=BCN_TELEGRAM_TIFA_TOKEN
60
+ --set channel.token_env=BCN_TELEGRAM_TIFA_TOKEN \
61
+ --set channel.allowed_sender_ids=[123456789]
59
62
 
60
63
  # Configure the runtime environment / 配置运行时环境
61
64
  # Example file content / 示例文件内容:
@@ -32,11 +32,14 @@ bcn --version
32
32
  uv tool upgrade bazaar-compute-node
33
33
 
34
34
  # Add an Agent / 添加 Agent
35
+ # allowed_sender_ids holds the Telegram user ids allowed to open a private chat
36
+ # allowed_sender_ids 是允许与其私聊的 Telegram 用户 id
35
37
  bcn agent add \
36
38
  --name Tifa \
37
39
  --channel telegram \
38
40
  --runtime codex \
39
- --set channel.token_env=BCN_TELEGRAM_TIFA_TOKEN
41
+ --set channel.token_env=BCN_TELEGRAM_TIFA_TOKEN \
42
+ --set channel.allowed_sender_ids=[123456789]
40
43
 
41
44
  # Configure the runtime environment / 配置运行时环境
42
45
  # Example file content / 示例文件内容:
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "bazaar-compute-node"
3
- version = "0.2.0rc1"
3
+ version = "0.2.0rc3"
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.2.0rc1"
3
+ version = "0.2.0rc3"
4
4
  description = "Runtime-agnostic computer node daemon for agents and channels."
5
5
  readme = "README.md"
6
6
  license = "AGPL-3.0-only"
@@ -13,6 +13,7 @@ from functools import partial
13
13
  from pathlib import Path
14
14
 
15
15
  from ..core.actor import Actor, Actors, Agent, Thread
16
+ from ..core.audit import AuditRecorder
16
17
  from ..core.channel import Channel, ChannelContext, IChannel
17
18
  from ..core.concurrency import IThreadConcurrency, ThreadLockRegistry
18
19
  from ..core.lifecycle import TimeoutBudget
@@ -28,6 +29,7 @@ from ..core.paths import resolve_workspace_dir
28
29
  from ..core.runtime import IRuntime, RuntimeCommandContext
29
30
  from ..core.storage import IStorageScope
30
31
  from ..core.timerwheel import TimerWheel
32
+ from ..core.utils.clock import now_ms
31
33
  from ..i18n import Translator
32
34
  from .attachments import AttachmentMaterializer
33
35
  from .command import CommandDispatchError
@@ -104,6 +106,11 @@ class AgentApplication:
104
106
  self.workspace_path,
105
107
  self._referenced_attachment_paths,
106
108
  )
109
+ self._audit_recorder = AuditRecorder(
110
+ sink=audit,
111
+ timeout_budget=timeout_budget,
112
+ clock=now_ms,
113
+ )
107
114
  provider_channel = factories.channel.build(
108
115
  ChannelContext(
109
116
  agent_id=self.agent_id,
@@ -112,6 +119,7 @@ class AgentApplication:
112
119
  workspace=self.workspace_path,
113
120
  translator=self.translator,
114
121
  timer_wheel=self.timer_wheel,
122
+ audit=self._audit_recorder,
115
123
  )
116
124
  )
117
125
  self.channel: IChannel = Channel(self.agent_id, provider_channel)
@@ -163,7 +171,7 @@ class AgentApplication:
163
171
  channel=self.channel,
164
172
  runtimes=self.runtimes,
165
173
  storage=self.storage,
166
- audit=self.audit,
174
+ audit=self._audit_recorder,
167
175
  timeout_budget=self.timeout_budget,
168
176
  timer_wheel=self.timer_wheel,
169
177
  runtime_idle_timeout_ms=(
@@ -15,6 +15,15 @@ import aiohttp
15
15
  from ...core.timerwheel import TimerWheel
16
16
  from ...core.utils.clock import remaining
17
17
 
18
+
19
+ @dataclass(frozen=True, slots=True)
20
+ class LarkSentMessage:
21
+ """What the provider says about a message it just accepted."""
22
+
23
+ message_id: str
24
+ chat_id: str | None
25
+
26
+
18
27
  _CONNECT_TIMEOUT_SECONDS = 10.0
19
28
  _HTTP_METHODS = frozenset({"GET", "POST", "PUT", "DELETE"})
20
29
  _MAX_ERROR_MESSAGE = 256
@@ -412,12 +421,13 @@ class LarkApi:
412
421
  content: str,
413
422
  uuid: str,
414
423
  timeout: float,
415
- ) -> str:
424
+ receive_id_type: str = "chat_id",
425
+ ) -> LarkSentMessage:
416
426
  body = await self._post_json(
417
427
  "message_create",
418
428
  "/open-apis/im/v1/messages",
419
429
  timeout=timeout,
420
- params={"receive_id_type": "chat_id"},
430
+ params={"receive_id_type": receive_id_type},
421
431
  json_body={
422
432
  "receive_id": chat_id,
423
433
  "msg_type": message_type,
@@ -425,7 +435,10 @@ class LarkApi:
425
435
  "uuid": uuid,
426
436
  },
427
437
  )
428
- return _response_id(body, "message_create", "message_id")
438
+ return LarkSentMessage(
439
+ message_id=_response_id(body, "message_create", "message_id"),
440
+ chat_id=_optional_response_text(body, "chat_id"),
441
+ )
429
442
 
430
443
  async def reply_message(
431
444
  self,
@@ -436,7 +449,7 @@ class LarkApi:
436
449
  reply_in_thread: bool,
437
450
  uuid: str,
438
451
  timeout: float,
439
- ) -> str:
452
+ ) -> LarkSentMessage:
440
453
  body = await self._post_json(
441
454
  "message_reply",
442
455
  f"/open-apis/im/v1/messages/{quote(message_id, safe='')}/reply",
@@ -448,7 +461,10 @@ class LarkApi:
448
461
  "uuid": uuid,
449
462
  },
450
463
  )
451
- return _response_id(body, "message_reply", "message_id")
464
+ return LarkSentMessage(
465
+ message_id=_response_id(body, "message_reply", "message_id"),
466
+ chat_id=_optional_response_text(body, "chat_id"),
467
+ )
452
468
 
453
469
  async def reply_card(
454
470
  self,
@@ -458,7 +474,7 @@ class LarkApi:
458
474
  reply_in_thread: bool,
459
475
  uuid: str,
460
476
  timeout: float,
461
- ) -> str:
477
+ ) -> LarkSentMessage:
462
478
  return await self.reply_message(
463
479
  message_id=message_id,
464
480
  message_type="interactive",
@@ -913,6 +929,17 @@ def _provider_message(value: object) -> str:
913
929
  return value[:_MAX_ERROR_MESSAGE]
914
930
 
915
931
 
932
+ def _optional_response_text(
933
+ body: Mapping[str, object],
934
+ field_name: str,
935
+ ) -> str | None:
936
+ data = body.get("data")
937
+ value = data.get(field_name) if isinstance(data, Mapping) else None
938
+ if not isinstance(value, str) or not value or "\r" in value or "\n" in value:
939
+ return None
940
+ return value
941
+
942
+
916
943
  def _response_id(
917
944
  body: Mapping[str, object],
918
945
  method: str,
@@ -36,6 +36,7 @@ class _PendingApproval:
36
36
  token: str
37
37
  thread: LarkThreadIdentity
38
38
  expected_sender_id: str
39
+ callback_chat_id: str
39
40
  prompt_message_id: str | None
40
41
  future: asyncio.Future[ApprovalResult]
41
42
 
@@ -135,6 +136,7 @@ class LarkApprovalChannel(LarkChannel):
135
136
  token=secrets.token_urlsafe(18),
136
137
  thread=thread,
137
138
  expected_sender_id=request.provider_sender_id,
139
+ callback_chat_id=thread.chat_id,
138
140
  prompt_message_id=None,
139
141
  future=loop.create_future(),
140
142
  )
@@ -154,7 +156,7 @@ class LarkApprovalChannel(LarkChannel):
154
156
  )
155
157
  budget = remaining(deadline)
156
158
  if reply_to_message_id is not None:
157
- prompt_message_id = await api.reply_message(
159
+ sent = await api.reply_message(
158
160
  message_id=reply_to_message_id,
159
161
  message_type="interactive",
160
162
  content=content,
@@ -163,14 +165,22 @@ class LarkApprovalChannel(LarkChannel):
163
165
  timeout=budget,
164
166
  )
165
167
  else:
166
- prompt_message_id = await api.send_message(
168
+ sent = await api.send_message(
167
169
  chat_id=thread.chat_id,
170
+ # A one-to-one chat is named by the peer, so the id that
171
+ # addresses it is an `ou_`; card callbacks report the `oc_`
172
+ # the provider gave that chat, which only the send tells us.
173
+ receive_id_type=(
174
+ "open_id" if thread.chat_id.startswith("ou_") else "chat_id"
175
+ ),
168
176
  message_type="interactive",
169
177
  content=content,
170
178
  uuid=uuid4().hex,
171
179
  timeout=budget,
172
180
  )
173
- prompt_message_id = _provider_text(prompt_message_id)
181
+ if sent.chat_id is not None:
182
+ pending.callback_chat_id = sent.chat_id
183
+ prompt_message_id = _provider_text(sent.message_id)
174
184
  if prompt_message_id is None:
175
185
  raise ValueError("Lark approval prompt has no message_id")
176
186
  if (
@@ -237,7 +247,7 @@ class LarkApprovalChannel(LarkChannel):
237
247
  resolved_callback_text(self._translator, state),
238
248
  toast_type="warning",
239
249
  )
240
- if chat_id != pending.thread.chat_id or (
250
+ if chat_id != pending.callback_chat_id or (
241
251
  pending.prompt_message_id is not None
242
252
  and message_id != pending.prompt_message_id
243
253
  ):
@@ -17,6 +17,7 @@ from ...core.channel import (
17
17
  ChannelDeliveryReceipt,
18
18
  ChannelIdentity,
19
19
  ChannelSendRequest,
20
+ DmAddress,
20
21
  IChannel,
21
22
  )
22
23
  from ...core.models import (
@@ -414,7 +415,15 @@ class LarkChannel(IChannel):
414
415
  )
415
416
  thread_identity = LarkThreadIdentity(
416
417
  bot_open_id=fields.identity.open_id,
417
- chat_id=fields.chat_id,
418
+ # A one-to-one chat is named by the peer, not by the `oc_` id the
419
+ # provider gives that chat: the peer is the only part of it we can
420
+ # know before the chat exists, so a conversation opened from
421
+ # `dm:@name` and one the peer starts are the same conversation.
422
+ chat_id=(
423
+ fields.sender_open_id
424
+ if target_kind is ChannelTargetKind.DM
425
+ else fields.chat_id
426
+ ),
418
427
  thread_id=thread_id,
419
428
  )
420
429
  presentation = None
@@ -954,6 +963,20 @@ class LarkChannel(IChannel):
954
963
  except LarkApiError, LarkTransportError, TimeoutError:
955
964
  self._typing_failures += 1
956
965
 
966
+ def dm_address(
967
+ self, sender: SenderIdentity, *, sender_kind: SenderKind
968
+ ) -> DmAddress | None:
969
+ del sender_kind
970
+ identity = self._identity
971
+ if identity is None or sender.id is None:
972
+ return None
973
+ thread = LarkThreadIdentity(bot_open_id=identity.open_id, chat_id=sender.id)
974
+ return DmAddress(
975
+ channel_session_id=thread.channel_session_id,
976
+ thread_id=thread.session_id,
977
+ provider_thread_id=thread.provider_thread_id,
978
+ )
979
+
957
980
  async def send(
958
981
  self,
959
982
  request: ChannelSendRequest,
@@ -1089,6 +1112,7 @@ class _EventFields:
1089
1112
  provider_message_id: str
1090
1113
  chat_id: str
1091
1114
  chat_type: str
1115
+ sender_open_id: str
1092
1116
  sender_type: str
1093
1117
 
1094
1118
 
@@ -1147,6 +1171,7 @@ def _read_event(
1147
1171
  provider_message_id=provider_message_id,
1148
1172
  chat_id=chat_id,
1149
1173
  chat_type=chat_type,
1174
+ sender_open_id=sender_open_id,
1150
1175
  sender_type=sender_type,
1151
1176
  )
1152
1177
 
@@ -375,7 +375,7 @@ async def _send_message_part(
375
375
  return None, TimeoutError("Lark delivery deadline expired")
376
376
  try:
377
377
  if reply_anchor is not None:
378
- provider_message_id = await api.reply_message(
378
+ sent = await api.reply_message(
379
379
  message_id=reply_anchor,
380
380
  message_type=message_type,
381
381
  content=content,
@@ -384,8 +384,12 @@ async def _send_message_part(
384
384
  timeout=timeout,
385
385
  )
386
386
  else:
387
- provider_message_id = await api.send_message(
387
+ sent = await api.send_message(
388
388
  chat_id=thread.chat_id,
389
+ # Lark ids carry their kind in the prefix: `ou_` a user, `oc_` a chat.
390
+ receive_id_type=(
391
+ "open_id" if thread.chat_id.startswith("ou_") else "chat_id"
392
+ ),
389
393
  message_type=message_type,
390
394
  content=content,
391
395
  uuid=str(uuid4()),
@@ -395,7 +399,7 @@ async def _send_message_part(
395
399
  raise
396
400
  except Exception as error: # noqa: BLE001
397
401
  return None, error
398
- return provider_message_id, None
402
+ return sent.message_id, None
399
403
 
400
404
 
401
405
  def _finish_failure(
@@ -21,7 +21,9 @@ from ....core.models import (
21
21
  SenderKind,
22
22
  )
23
23
  from ....core.storage import (
24
+ AmbiguousInboxTargetError,
24
25
  InboxTargetResolutionError,
26
+ KnownSender,
25
27
  ResolvedInboxTarget,
26
28
  UnreadMessageOwner,
27
29
  )
@@ -331,9 +333,13 @@ class MessageOperations(RepositoryBase):
331
333
  f"AND ({predicate}) ORDER BY thread.id",
332
334
  parameters,
333
335
  )
334
- if len(rows) != 1:
336
+ if len(rows) > 1:
337
+ raise AmbiguousInboxTargetError(
338
+ "inbox target resolves to more than one owned session"
339
+ )
340
+ if not rows:
335
341
  raise InboxTargetResolutionError(
336
- "inbox target does not resolve to exactly one owned session"
342
+ "inbox target does not resolve to an owned session"
337
343
  )
338
344
  target = thread_from_row(rows[0])
339
345
  channel_session = cast(
@@ -363,6 +369,54 @@ class MessageOperations(RepositoryBase):
363
369
  handle_is_unique=handle_is_unique,
364
370
  )
365
371
 
372
+ async def find_known_sender(self, token: str) -> KnownSender | None:
373
+ """Find a sender this Agent has heard from, by what its `@` renders as.
374
+
375
+ `sender` carries the handle when the provider offers one and `sender_id`
376
+ the provider identity otherwise, which is the same order the `@` position
377
+ is rendered in. Matching follows that order so the more specific value
378
+ decides first. The display name is deliberately not a key: it is not
379
+ unique, and addressing by it would let one name reach several people.
380
+ """
381
+
382
+ for predicate, parameter in (
383
+ ("LOWER(sender) = LOWER(?)", token),
384
+ ("sender_id = ?", token),
385
+ ):
386
+ row = await self.fetchone(
387
+ "SELECT sender, sender_id, sender_display_name, channel, "
388
+ "metadata_json "
389
+ "FROM messages "
390
+ "WHERE agent_id = /*agent_id*/? "
391
+ "AND direction = ? "
392
+ f"AND {predicate} "
393
+ "ORDER BY seq DESC LIMIT 1",
394
+ (MessageDirection.INBOUND.value, parameter),
395
+ )
396
+ if row is None:
397
+ continue
398
+ sender = cast(str | None, row["sender"])
399
+ sender_id = cast(str | None, row["sender_id"])
400
+ if sender is None and sender_id is None:
401
+ continue
402
+ metadata_json = cast(str | None, row["metadata_json"])
403
+ sender_kind = SenderKind.UNKNOWN
404
+ if metadata_json:
405
+ stored_kind = json.loads(metadata_json).get(
406
+ "sender_kind", SenderKind.UNKNOWN.value
407
+ )
408
+ sender_kind = SenderKind(cast(str, stored_kind))
409
+ return KnownSender(
410
+ sender=SenderIdentity(
411
+ id=sender_id,
412
+ name=sender,
413
+ display_name=cast(str | None, row["sender_display_name"]),
414
+ ),
415
+ channel=cast(str, row["channel"]),
416
+ sender_kind=sender_kind,
417
+ )
418
+ return None
419
+
366
420
  async def find_message(
367
421
  self,
368
422
  channel: str,
@@ -13,6 +13,7 @@ _READ_OPERATIONS = frozenset(
13
13
  "count_messages",
14
14
  "find_thread",
15
15
  "find_channel_session",
16
+ "find_known_sender",
16
17
  "find_message",
17
18
  "get_thread",
18
19
  "get_channel_session",
@@ -87,12 +87,15 @@ class TelegramBotApi:
87
87
  async def send_chat_action(
88
88
  self,
89
89
  *,
90
- chat_id: int,
90
+ chat_id: int | str,
91
91
  action: str,
92
92
  message_thread_id: int | None = None,
93
93
  timeout: float,
94
94
  ) -> None:
95
- if not isinstance(chat_id, int) or isinstance(chat_id, bool) or chat_id == 0:
95
+ if isinstance(chat_id, str):
96
+ if not chat_id:
97
+ raise ValueError("Telegram chat_id text must be non-empty")
98
+ elif not isinstance(chat_id, int) or isinstance(chat_id, bool) or chat_id == 0:
96
99
  raise ValueError("Telegram chat_id must be a non-zero integer")
97
100
  if not isinstance(action, str) or not action:
98
101
  raise ValueError("Telegram chat action must be non-empty")
@@ -27,7 +27,7 @@ _RESOLVED_TOKEN_LIMIT = 256
27
27
  class _PendingApproval:
28
28
  request_id: str
29
29
  token: str
30
- chat_id: int
30
+ chat_id: int | str
31
31
  topic_id: int
32
32
  markdown: str
33
33
  prompt_message_id: int | None
@@ -36,8 +36,14 @@ class _PendingApproval:
36
36
 
37
37
 
38
38
  class TelegramApprovalChannel(TelegramChannel):
39
- def __init__(self, context: ChannelContext, *, token: str) -> None:
40
- super().__init__(context, token=token)
39
+ def __init__(
40
+ self,
41
+ context: ChannelContext,
42
+ *,
43
+ token: str,
44
+ allowed_sender_ids: frozenset[int],
45
+ ) -> None:
46
+ super().__init__(context, token=token, allowed_sender_ids=allowed_sender_ids)
41
47
  self._translator: Translator = context.translator or create_translator(ENGLISH)
42
48
  self._pending_approvals: dict[str, _PendingApproval] = {}
43
49
  self._approval_tokens_by_request: dict[str, str] = {}
@@ -15,8 +15,10 @@ from ...core.channel import (
15
15
  ChannelDeliveryReceipt,
16
16
  ChannelIdentity,
17
17
  ChannelSendRequest,
18
+ DmAddress,
18
19
  IChannel,
19
20
  )
21
+ from ...core.correlation import CorrelationContext
20
22
  from ...core.models import (
21
23
  ApprovalDecision,
22
24
  ApprovalResult,
@@ -28,6 +30,7 @@ from ...core.models import (
28
30
  InboundAttachment,
29
31
  Message,
30
32
  MessageDirection,
33
+ RuntimeEventState,
31
34
  RuntimeOutputEvent,
32
35
  SenderIdentity,
33
36
  SenderKind,
@@ -44,6 +47,7 @@ from ...core.models import (
44
47
  TurnUnknown,
45
48
  UsageUpdated,
46
49
  )
50
+ from ...core.observability import LogLevel
47
51
  from ...core.outcomes import ProviderCallResult, ProviderCallStatus
48
52
  from ...core.timerwheel import TimerWheel
49
53
  from .api import TelegramApiError, TelegramBotApi, TelegramTransportError
@@ -95,9 +99,16 @@ class _InboundUpdate:
95
99
 
96
100
 
97
101
  class TelegramChannel(IChannel):
98
- def __init__(self, context: ChannelContext, *, token: str) -> None:
102
+ def __init__(
103
+ self,
104
+ context: ChannelContext,
105
+ *,
106
+ token: str,
107
+ allowed_sender_ids: frozenset[int],
108
+ ) -> None:
99
109
  self._context = context
100
110
  self._token = token
111
+ self._allowed_sender_ids = allowed_sender_ids
101
112
  self._timer_wheel: TimerWheel | None = context.timer_wheel
102
113
  self._started_at_s = time_ns() // 1_000_000_000
103
114
  self._inbound: asyncio.Queue[Message | object] = asyncio.Queue()
@@ -431,6 +442,37 @@ class TelegramChannel(IChannel):
431
442
  exc_info=(type(error), error, error.__traceback__),
432
443
  )
433
444
 
445
+ def dm_address(
446
+ self, sender: SenderIdentity, *, sender_kind: SenderKind
447
+ ) -> DmAddress | None:
448
+ bot_id = self._bot_id
449
+ if bot_id is None:
450
+ return None
451
+ chat_id: int | str
452
+ # sendMessage accepts an @username only for a bot, supergroup or
453
+ # channel; a person is still addressable by numeric id alone.
454
+ if sender_kind is SenderKind.AGENT and sender.name is not None:
455
+ chat_id = f"@{sender.name}"
456
+ # A message a channel or an anonymous admin posted to a group carries
457
+ # that chat's id and no `from`, so its kind is unknown; addressing it
458
+ # would publish the DM back into the group it came from.
459
+ elif (
460
+ sender_kind in {SenderKind.HUMAN, SenderKind.AGENT}
461
+ and sender.id is not None
462
+ ):
463
+ try:
464
+ chat_id = int(sender.id)
465
+ except ValueError:
466
+ return None
467
+ else:
468
+ return None
469
+ identity = TelegramThreadIdentity(bot_id=bot_id, chat_id=chat_id, topic_id=0)
470
+ return DmAddress(
471
+ channel_session_id=identity.channel_session_id,
472
+ thread_id=identity.session_id,
473
+ provider_thread_id=identity.provider_thread_id,
474
+ )
475
+
434
476
  async def send(
435
477
  self,
436
478
  request: ChannelSendRequest,
@@ -653,6 +695,31 @@ class TelegramChannel(IChannel):
653
695
  chat_type = chat.get("type")
654
696
  if chat_type not in {"private", "group", "supergroup"}:
655
697
  return "unsupported_chat_type"
698
+ # Anyone on Telegram can open a private chat with a bot. A group has to
699
+ # be joined, so only the private case is closed here.
700
+ if chat_type == "private" and not (
701
+ isinstance(provider_sender, Mapping)
702
+ and provider_sender.get("id") in self._allowed_sender_ids
703
+ ):
704
+ audit = self._context.audit
705
+ if audit is not None:
706
+ await audit.append(
707
+ event_name="channel.inbound.rejected",
708
+ state=RuntimeEventState.COMPLETED,
709
+ correlation=CorrelationContext(
710
+ node_id=self._context.agent_id,
711
+ channel=self.name,
712
+ ),
713
+ level=LogLevel.WARNING,
714
+ metadata={
715
+ "reason": "unauthorized_sender",
716
+ "telegram_sender_id": provider_sender.get("id")
717
+ if isinstance(provider_sender, Mapping)
718
+ else None,
719
+ "telegram_chat_id": chat.get("id"),
720
+ },
721
+ )
722
+ return "unauthorized_sender"
656
723
  chat_id = chat.get("id")
657
724
  provider_message_id = message.get("message_id")
658
725
  provider_time_s = message.get("date")
@@ -8,14 +8,24 @@ _PROVIDER_PREFIX = "telegram"
8
8
 
9
9
  @dataclass(frozen=True, slots=True)
10
10
  class TelegramThreadIdentity:
11
+ """A Telegram conversation, addressed the way Telegram itself addresses it.
12
+
13
+ `chat_id` is the numeric id of a chat this bot has seen, or an `@username`
14
+ for a peer it has not: Bot API accepts either as `chat_id`, and a username
15
+ is the only address available before the first message arrives.
16
+ """
17
+
11
18
  bot_id: int
12
- chat_id: int
19
+ chat_id: int | str
13
20
  topic_id: int
14
21
 
15
22
  def __post_init__(self) -> None:
16
23
  if self.bot_id <= 0:
17
24
  raise ValueError("Telegram bot_id must be positive")
18
- if self.chat_id == 0:
25
+ if isinstance(self.chat_id, str):
26
+ if not self.chat_id.startswith("@") or len(self.chat_id) < 2:
27
+ raise ValueError("Telegram chat_id text must be an @username")
28
+ elif self.chat_id == 0:
19
29
  raise ValueError("Telegram chat_id must be non-zero")
20
30
  if self.topic_id < 0:
21
31
  raise ValueError("Telegram topic_id must be non-negative")
@@ -56,12 +66,18 @@ def parse_provider_thread_id(value: str) -> TelegramThreadIdentity:
56
66
  parts = value.split(":")
57
67
  if len(parts) != 4 or parts[0] != _PROVIDER_PREFIX:
58
68
  raise ValueError("Telegram provider_thread_id has invalid format")
69
+ chat_id: int | str = parts[2]
59
70
  try:
60
- bot_id, chat_id, topic_id = (int(part) for part in parts[1:])
71
+ bot_id = int(parts[1])
72
+ topic_id = int(parts[3])
61
73
  except ValueError as error:
62
74
  raise ValueError(
63
75
  "Telegram provider_thread_id contains invalid integers"
64
76
  ) from error
77
+ try:
78
+ chat_id = int(chat_id)
79
+ except ValueError:
80
+ pass
65
81
  return TelegramThreadIdentity(bot_id=bot_id, chat_id=chat_id, topic_id=topic_id)
66
82
 
67
83
 
@@ -64,8 +64,9 @@ class TelegramOutboundChannel(TelegramApprovalChannel):
64
64
  context: ChannelContext,
65
65
  *,
66
66
  token: str,
67
+ allowed_sender_ids: frozenset[int],
67
68
  ) -> None:
68
- super().__init__(context, token=token)
69
+ super().__init__(context, token=token, allowed_sender_ids=allowed_sender_ids)
69
70
  self._outbound_requests = 0
70
71
  self._outbound_confirmed_requests = 0
71
72
  self._outbound_partial_requests = 0