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.
- {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/PKG-INFO +1 -1
- {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/pyproject.toml +1 -1
- {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/pyproject.toml.orig +1 -1
- {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/app/agent.py +11 -36
- {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/app/application.py +10 -5
- {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/app/command.py +128 -74
- {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/app/resource_dispatch.py +3 -197
- {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/bcc.py +5 -216
- {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/contrib/lark/channel.py +12 -8
- {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/contrib/sqlite/codec.py +132 -161
- bazaar_compute_node-0.1.27/src/bazaar_compute_node/contrib/sqlite/database.py +288 -0
- bazaar_compute_node-0.1.27/src/bazaar_compute_node/contrib/sqlite/executor.py +544 -0
- bazaar_compute_node-0.1.27/src/bazaar_compute_node/contrib/sqlite/migrations/__init__.py +59 -0
- bazaar_compute_node-0.1.27/src/bazaar_compute_node/contrib/sqlite/migrations/registry.py +172 -0
- 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
- bazaar_compute_node-0.1.27/src/bazaar_compute_node/contrib/sqlite/migrations/v02_session_mapping_indexes.py +31 -0
- bazaar_compute_node-0.1.27/src/bazaar_compute_node/contrib/sqlite/migrations/v03_message_log_indexes.py +23 -0
- bazaar_compute_node-0.1.27/src/bazaar_compute_node/contrib/sqlite/migrations/v04_runtime_attempt_facts.py +46 -0
- bazaar_compute_node-0.1.27/src/bazaar_compute_node/contrib/sqlite/migrations/v05_inbound_message_references.py +33 -0
- bazaar_compute_node-0.1.27/src/bazaar_compute_node/contrib/sqlite/migrations/v06_inbound_message_reference_integrity.py +25 -0
- bazaar_compute_node-0.1.27/src/bazaar_compute_node/contrib/sqlite/migrations/v07_inbound_provider_identity.py +24 -0
- bazaar_compute_node-0.1.27/src/bazaar_compute_node/contrib/sqlite/migrations/v08_transient_stream_events.py +29 -0
- bazaar_compute_node-0.1.27/src/bazaar_compute_node/contrib/sqlite/migrations/v09_remove_runtime_events.py +20 -0
- bazaar_compute_node-0.1.27/src/bazaar_compute_node/contrib/sqlite/migrations/v10_add_outbound_attachments.py +17 -0
- bazaar_compute_node-0.1.27/src/bazaar_compute_node/contrib/sqlite/migrations/v11_remove_runtime_session_mapping.py +19 -0
- 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
- 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
- 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
- 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
- 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
- bazaar_compute_node-0.1.27/src/bazaar_compute_node/contrib/sqlite/migrations/v17_remove_agent_identity_triggers.py +20 -0
- bazaar_compute_node-0.1.27/src/bazaar_compute_node/contrib/sqlite/migrations/v18_unify_messages.py +400 -0
- bazaar_compute_node-0.1.27/src/bazaar_compute_node/contrib/sqlite/migrations/v19_reminder_system_messages.py +226 -0
- bazaar_compute_node-0.1.27/src/bazaar_compute_node/contrib/sqlite/migrations/v20_remove_freshness_persistence.py +162 -0
- bazaar_compute_node-0.1.27/src/bazaar_compute_node/contrib/sqlite/migrations/v21_remove_handoffs.py +14 -0
- {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/contrib/sqlite/plugin.py +2 -1
- {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/contrib/sqlite/reminder_codec.py +2 -19
- bazaar_compute_node-0.1.27/src/bazaar_compute_node/contrib/sqlite/repository/__init__.py +5 -0
- bazaar_compute_node-0.1.27/src/bazaar_compute_node/contrib/sqlite/repository/base.py +187 -0
- bazaar_compute_node-0.1.27/src/bazaar_compute_node/contrib/sqlite/repository/facade.py +141 -0
- bazaar_compute_node-0.1.27/src/bazaar_compute_node/contrib/sqlite/repository/messages.py +921 -0
- bazaar_compute_node-0.1.27/src/bazaar_compute_node/contrib/sqlite/repository/reminders.py +386 -0
- bazaar_compute_node-0.1.27/src/bazaar_compute_node/contrib/sqlite/repository/sessions.py +261 -0
- bazaar_compute_node-0.1.27/src/bazaar_compute_node/contrib/sqlite/storage.py +141 -0
- {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/contrib/telegram/channel.py +13 -8
- {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/contrib/wecom/channel.py +13 -10
- {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/core/channel.py +4 -4
- {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/core/command.py +31 -49
- {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/core/instruction.py +18 -53
- {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/core/models/__init__.py +8 -10
- {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/core/models/entities.py +102 -190
- {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/core/models/reminder.py +0 -76
- bazaar_compute_node-0.1.27/src/bazaar_compute_node/core/models/reminder_owner.py +28 -0
- {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/core/models/states.py +13 -18
- {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/core/orchestration/command.py +166 -291
- {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/core/orchestration/error_feedback.py +6 -3
- {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/core/orchestration/reminder.py +71 -48
- {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/core/orchestration/reminder_command.py +34 -118
- {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/core/orchestration/services.py +3 -4
- {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/core/orchestration/session.py +112 -331
- {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/core/orchestration/turn.py +70 -31
- {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/core/reminder.py +25 -37
- bazaar_compute_node-0.1.27/src/bazaar_compute_node/core/storage.py +688 -0
- {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/i18n/english.py +0 -1
- {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/i18n/schinese.py +0 -1
- bazaar_compute_node-0.1.25/src/bazaar_compute_node/contrib/sqlite/database.py +0 -220
- bazaar_compute_node-0.1.25/src/bazaar_compute_node/contrib/sqlite/handoff_codec.py +0 -24
- bazaar_compute_node-0.1.25/src/bazaar_compute_node/contrib/sqlite/handoff_repository.py +0 -166
- bazaar_compute_node-0.1.25/src/bazaar_compute_node/contrib/sqlite/reminder_repository.py +0 -588
- bazaar_compute_node-0.1.25/src/bazaar_compute_node/contrib/sqlite/repository.py +0 -806
- bazaar_compute_node-0.1.25/src/bazaar_compute_node/contrib/sqlite/scoped_repository.py +0 -931
- bazaar_compute_node-0.1.25/src/bazaar_compute_node/contrib/sqlite/storage.py +0 -74
- bazaar_compute_node-0.1.25/src/bazaar_compute_node/core/handoff.py +0 -49
- bazaar_compute_node-0.1.25/src/bazaar_compute_node/core/models/handoff.py +0 -26
- bazaar_compute_node-0.1.25/src/bazaar_compute_node/core/models/reminder_owner.py +0 -59
- bazaar_compute_node-0.1.25/src/bazaar_compute_node/core/orchestration/handoff_command.py +0 -301
- bazaar_compute_node-0.1.25/src/bazaar_compute_node/core/storage.py +0 -228
- {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/LICENSE +0 -0
- {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/README.md +0 -0
- {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/__init__.py +0 -0
- {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/app/__init__.py +0 -0
- {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/app/agent_management.py +0 -0
- {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/app/attachments.py +0 -0
- {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/app/config.py +0 -0
- {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/app/registry.py +0 -0
- {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/app/system_service.py +0 -0
- {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/app/transport.py +0 -0
- {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/app/windows_pipe.py +0 -0
- {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/app/wrapper.py +0 -0
- {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/cli.py +0 -0
- {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/contrib/__init__.py +0 -0
- {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/contrib/codex/__init__.py +0 -0
- {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/contrib/codex/approval.py +0 -0
- {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/contrib/codex/client.py +0 -0
- {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/contrib/codex/events.py +0 -0
- {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/contrib/codex/plugin.py +0 -0
- {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/contrib/codex/process.py +0 -0
- {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/contrib/codex/protocol.py +0 -0
- {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/contrib/codex/runtime.py +0 -0
- {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/contrib/lark/__init__.py +0 -0
- {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/contrib/lark/api.py +0 -0
- {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/contrib/lark/approval.py +0 -0
- {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/contrib/lark/attachments.py +0 -0
- {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/contrib/lark/frame.py +0 -0
- {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/contrib/lark/identity.py +0 -0
- {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/contrib/lark/outbound.py +0 -0
- {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/contrib/lark/plugin.py +0 -0
- {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/contrib/lark/transport.py +0 -0
- {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/contrib/logging/__init__.py +0 -0
- {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/contrib/logging/audit.py +0 -0
- {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/contrib/logging/plugin.py +0 -0
- {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/contrib/sqlite/__init__.py +0 -0
- /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
- {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/contrib/telegram/__init__.py +0 -0
- {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/contrib/telegram/api.py +0 -0
- {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/contrib/telegram/approval.py +0 -0
- {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/contrib/telegram/attachments.py +0 -0
- {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/contrib/telegram/identity.py +0 -0
- {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/contrib/telegram/markdown.py +0 -0
- {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/contrib/telegram/outbound.py +0 -0
- {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/contrib/telegram/plugin.py +0 -0
- {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/contrib/wecom/__init__.py +0 -0
- {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/contrib/wecom/markdown.py +0 -0
- {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/contrib/wecom/outbound.py +0 -0
- {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/contrib/wecom/plugin.py +0 -0
- {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/core/__init__.py +0 -0
- {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/core/approval.py +0 -0
- {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/core/audit.py +0 -0
- {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/core/client.py +0 -0
- {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/core/concurrency.py +0 -0
- {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/core/correlation.py +0 -0
- {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/core/inbox.py +0 -0
- {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/core/lifecycle.py +0 -0
- {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/core/observability.py +0 -0
- {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/core/orchestration/__init__.py +0 -0
- {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/core/orchestration/delivery.py +0 -0
- {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/core/outcomes.py +0 -0
- {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/core/paths.py +0 -0
- {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/core/reminder_schedule.py +0 -0
- {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/core/runtime.py +0 -0
- {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/core/timerwheel.py +0 -0
- {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/i18n/__init__.py +0 -0
- {bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/i18n/catalog.py +0 -0
{bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/app/agent.py
RENAMED
|
@@ -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
|
|
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
|
-
|
|
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
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
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(
|
{bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/app/application.py
RENAMED
|
@@ -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.
|
|
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
|
|
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.
|
|
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
|
)
|
{bazaar_compute_node-0.1.25 → bazaar_compute_node-0.1.27}/src/bazaar_compute_node/app/command.py
RENAMED
|
@@ -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
|
|
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
|
|
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.
|
|
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
|
-
|
|
450
|
+
serialize_message(message) for message in result.messages
|
|
408
451
|
],
|
|
409
452
|
"referenced_messages": [
|
|
410
|
-
|
|
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
|
-
|
|
482
|
+
serialize_message(message) for message in result.messages
|
|
440
483
|
],
|
|
441
484
|
"referenced_messages": [
|
|
442
|
-
|
|
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
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
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
|
|
518
|
+
elif delivery_state is OutboundDeliveryState.QUEUED:
|
|
479
519
|
text = (
|
|
480
520
|
f"Message queued to {result.target}. "
|
|
481
|
-
f"Message ID: {result.
|
|
521
|
+
f"Message ID: {result.message_id}"
|
|
482
522
|
)
|
|
483
|
-
elif
|
|
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
|
|
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
|
|
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: {
|
|
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
|
-
|
|
532
|
-
|
|
533
|
-
timestamp
|
|
534
|
-
|
|
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
|
-
|
|
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
|
|
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} (
|
|
618
|
+
rendered.append(f"{name} ({identity}path:{path})")
|
|
572
619
|
else:
|
|
573
620
|
error = cast(str, attachment["error"])
|
|
574
|
-
rendered.append(f"{name} (
|
|
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,
|
|
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
|
-
|
|
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,
|
|
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={
|
|
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 = [
|
|
697
|
+
messages = [serialize_message(message) for message in result.messages]
|
|
624
698
|
referenced_messages = [
|
|
625
|
-
|
|
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
|
-
)
|