@cello-protocol/cli 0.0.143 → 0.0.144
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.
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"assets.d.ts","sourceRoot":"","sources":["../../src/hermes/assets.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,sEAAsE;AACtE,eAAO,MAAM,kBAAkB,w4CA6B9B,CAAC;AAEF;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,qBAAqB,
|
|
1
|
+
{"version":3,"file":"assets.d.ts","sourceRoot":"","sources":["../../src/hermes/assets.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,sEAAsE;AACtE,eAAO,MAAM,kBAAkB,w4CA6B9B,CAAC;AAEF;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,qBAAqB,QAurCjC,CAAC;AAEF,wEAAwE;AACxE,eAAO,MAAM,eAAe,60GAiE3B,CAAC"}
|
package/dist/hermes/assets.js
CHANGED
|
@@ -150,6 +150,24 @@ CALL_TIMEOUT_SECONDS = 30.0
|
|
|
150
150
|
# Server-side wait for the adapter's own cello_receive. Short on purpose: the notification that
|
|
151
151
|
# triggered it means the content is already durable, so this is a fetch, not a poll.
|
|
152
152
|
RECEIVE_TIMEOUT_MS = 5000
|
|
153
|
+
|
|
154
|
+
# State notices that channel mode does NOT hand to the agent, because a message follows within
|
|
155
|
+
# about a second and the notice's only effect is to occupy the agent at exactly the moment the
|
|
156
|
+
# message needs it free. Observed live 2026-08-07: 'created' started a turn, the message then
|
|
157
|
+
# found the chat busy, and the whole feature fell back to the manual path - the one time it
|
|
158
|
+
# worked, the agent had answered the notice with a bare [SILENT] and freed itself in time. The
|
|
159
|
+
# difference between "it works" and "it does nothing" was that race.
|
|
160
|
+
#
|
|
161
|
+
# A DENYLIST, deliberately: an unrecognised state is DELIVERED. Terminal ones (sealed, closed,
|
|
162
|
+
# interrupted) carry the only information about themselves - nothing follows them - so dropping
|
|
163
|
+
# an unknown state would be the silent kind of wrong.
|
|
164
|
+
STATE_WAKES_SUPPRESSED_IN_CHANNEL = {"created"}
|
|
165
|
+
|
|
166
|
+
# When the chat is mid-turn, wait for it rather than immediately downgrading to a notice. Turns
|
|
167
|
+
# end in seconds; fetching DURING one is the one thing that can lose a message outright. Total
|
|
168
|
+
# patience is LIMIT x DELAY before the notice fallback.
|
|
169
|
+
BUSY_RETRY_LIMIT = 5
|
|
170
|
+
BUSY_RETRY_DELAY_SECONDS = 2.0
|
|
153
171
|
# Upper bound on one drain. A conversation that has been away a long time can have a lot
|
|
154
172
|
# queued; handing an agent an unbounded turn is its own failure. Hitting this logs loudly -
|
|
155
173
|
# a silent truncation would read as "the agent saw everything" when it did not.
|
|
@@ -285,6 +303,8 @@ class CelloAdapter(BasePlatformAdapter):
|
|
|
285
303
|
# may execute before there is one.
|
|
286
304
|
self._wake_queue: Optional[asyncio.Queue] = None
|
|
287
305
|
self._wake_task: Optional[asyncio.Task] = None
|
|
306
|
+
# Live references to pending busy-retry timers (see _requeue_wake_later).
|
|
307
|
+
self._retry_tasks: set = set()
|
|
288
308
|
self._pending: Dict[str, asyncio.Future] = {}
|
|
289
309
|
self._next_id = 1
|
|
290
310
|
self._closing = False
|
|
@@ -373,6 +393,12 @@ class CelloAdapter(BasePlatformAdapter):
|
|
|
373
393
|
for task in (self._read_task, self._reconnect_task, self._wake_task):
|
|
374
394
|
if task is not None and not task.done():
|
|
375
395
|
task.cancel()
|
|
396
|
+
# Pending retries name a chat that is going away; leaving them running would re-queue
|
|
397
|
+
# wakes against a dead socket after disconnect.
|
|
398
|
+
for task in list(self._retry_tasks):
|
|
399
|
+
if not task.done():
|
|
400
|
+
task.cancel()
|
|
401
|
+
self._retry_tasks.clear()
|
|
376
402
|
self._read_task = None
|
|
377
403
|
self._reconnect_task = None
|
|
378
404
|
self._wake_task = None
|
|
@@ -422,6 +448,29 @@ class CelloAdapter(BasePlatformAdapter):
|
|
|
422
448
|
if self._wake_task is None or self._wake_task.done():
|
|
423
449
|
self._wake_task = asyncio.create_task(self._wake_worker())
|
|
424
450
|
|
|
451
|
+
def _requeue_wake_later(self, frame: Dict[str, Any], delay: float) -> None:
|
|
452
|
+
"""Put a wake back on the queue after the given delay, off the worker.
|
|
453
|
+
|
|
454
|
+
A plain sleep inside the worker would stall EVERY other agent's wake behind this one
|
|
455
|
+
chat's turn, which is the opposite of what the retry is for.
|
|
456
|
+
"""
|
|
457
|
+
async def _later() -> None:
|
|
458
|
+
try:
|
|
459
|
+
await asyncio.sleep(delay)
|
|
460
|
+
if self._closing or self._wake_queue is None:
|
|
461
|
+
return
|
|
462
|
+
self._wake_queue.put_nowait(frame)
|
|
463
|
+
except asyncio.CancelledError:
|
|
464
|
+
raise
|
|
465
|
+
except Exception:
|
|
466
|
+
logger.exception("[cello] Failed to re-queue a wake after a busy turn")
|
|
467
|
+
|
|
468
|
+
task = asyncio.create_task(_later())
|
|
469
|
+
# Hold a reference: asyncio only keeps a WEAK one, so an un-held task can be garbage
|
|
470
|
+
# collected mid-sleep and the wake would vanish with it.
|
|
471
|
+
self._retry_tasks.add(task)
|
|
472
|
+
task.add_done_callback(self._retry_tasks.discard)
|
|
473
|
+
|
|
425
474
|
async def _wake_worker(self) -> None:
|
|
426
475
|
while True:
|
|
427
476
|
frame = await self._wake_queue.get()
|
|
@@ -715,6 +764,21 @@ class CelloAdapter(BasePlatformAdapter):
|
|
|
715
764
|
)
|
|
716
765
|
return
|
|
717
766
|
|
|
767
|
+
# The phantom doorbell. In channel mode a 'created' notice announces a conversation that
|
|
768
|
+
# the message arriving a second later announces better - and handing it to the agent
|
|
769
|
+
# starts a turn that makes the agent BUSY exactly when the message needs it free.
|
|
770
|
+
if (
|
|
771
|
+
self._delivery_mode == "channel"
|
|
772
|
+
and kind == "session_state_changed"
|
|
773
|
+
and _safe_scalar(data.get("state")) in STATE_WAKES_SUPPRESSED_IN_CHANNEL
|
|
774
|
+
):
|
|
775
|
+
logger.debug(
|
|
776
|
+
"[cello] Not waking the agent for a '%s' state notice on session %s - the message "
|
|
777
|
+
"that follows carries everything it says", _safe_scalar(data.get("state")),
|
|
778
|
+
session_id,
|
|
779
|
+
)
|
|
780
|
+
return
|
|
781
|
+
|
|
718
782
|
counterparty = self._counterparty_of(kind, data)
|
|
719
783
|
chat_id = self._chat_id_for(counterparty)
|
|
720
784
|
if chat_id is None:
|
|
@@ -763,12 +827,32 @@ class CelloAdapter(BasePlatformAdapter):
|
|
|
763
827
|
# not provide is worse than no check. Computed once here and reused below.
|
|
764
828
|
session_key = self._session_key_for(source)
|
|
765
829
|
text = None
|
|
766
|
-
if
|
|
767
|
-
self.
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
830
|
+
if self._delivery_mode == "channel" and kind == "cello_message":
|
|
831
|
+
if session_key in self._active_sessions:
|
|
832
|
+
# BUSY: wait for the turn rather than downgrading on the spot. Immediately falling
|
|
833
|
+
# back to the notice sent the agent down the manual path for every message that
|
|
834
|
+
# happened to land mid-turn - which is most of them, since the agent is busy more
|
|
835
|
+
# often than not. Retrying costs the peer a couple of seconds; the alternative
|
|
836
|
+
# costs them the feature. Fetching anyway is NOT an option: it consumes the
|
|
837
|
+
# message, and a busy chat's queued event can be merged or replaced.
|
|
838
|
+
attempts = frame.get("_cello_busy_retries", 0)
|
|
839
|
+
if isinstance(attempts, int) and attempts < BUSY_RETRY_LIMIT:
|
|
840
|
+
frame["_cello_busy_retries"] = attempts + 1
|
|
841
|
+
logger.debug(
|
|
842
|
+
"[cello] %s is mid-turn; re-trying the fetch for session %s in %.0fs "
|
|
843
|
+
"(attempt %d of %d)",
|
|
844
|
+
session_key, session_id, BUSY_RETRY_DELAY_SECONDS,
|
|
845
|
+
attempts + 1, BUSY_RETRY_LIMIT,
|
|
846
|
+
)
|
|
847
|
+
self._requeue_wake_later(frame, BUSY_RETRY_DELAY_SECONDS)
|
|
848
|
+
return
|
|
849
|
+
logger.info(
|
|
850
|
+
"[cello] %s stayed mid-turn for %.0fs; handing the agent a notice for session "
|
|
851
|
+
"%s instead of the message, so it can still read it with the cello_* tools",
|
|
852
|
+
session_key, BUSY_RETRY_LIMIT * BUSY_RETRY_DELAY_SECONDS, session_id,
|
|
853
|
+
)
|
|
854
|
+
else:
|
|
855
|
+
text = await self._fetch_content(session_id)
|
|
772
856
|
if text is None:
|
|
773
857
|
text = self._wake_prompt(kind, data)
|
|
774
858
|
event = MessageEvent(
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"assets.js","sourceRoot":"","sources":["../../src/hermes/assets.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,sEAAsE;AACtE,MAAM,CAAC,MAAM,kBAAkB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA6BjC,CAAC;AAEF;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG,MAAM,CAAC,GAAG,CAAA
|
|
1
|
+
{"version":3,"file":"assets.js","sourceRoot":"","sources":["../../src/hermes/assets.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,sEAAsE;AACtE,MAAM,CAAC,MAAM,kBAAkB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA6BjC,CAAC;AAEF;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG,MAAM,CAAC,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAurC9C,CAAC;AAEF,wEAAwE;AACxE,MAAM,CAAC,MAAM,eAAe,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAiE9B,CAAC"}
|