diff --git a/nanobot/agent/loop.py b/nanobot/agent/loop.py index e90b30387..ad4b6d0dd 100644 --- a/nanobot/agent/loop.py +++ b/nanobot/agent/loop.py @@ -1283,6 +1283,7 @@ class AgentLoop: "assistant", result.content, _command=True ) self.sessions.save(ctx.session) + self._clear_pending_user_turn(ctx.session) return "shortcut" return "dispatch" diff --git a/nanobot/channels/base.py b/nanobot/channels/base.py index 58e72c18b..ed5c54232 100644 --- a/nanobot/channels/base.py +++ b/nanobot/channels/base.py @@ -195,11 +195,11 @@ class BaseChannel(ABC): """ if isinstance(self.config, dict): if "allow_from" in self.config: - allow_list = self.config.get("allow_from") + allow_list = self.config.get("allow_from") or [] else: - allow_list = self.config.get("allowFrom", []) + allow_list = self.config.get("allowFrom", []) or [] else: - allow_list = getattr(self.config, "allow_from", []) + allow_list = getattr(self.config, "allow_from", []) or [] if "*" in allow_list: return True if str(sender_id) in allow_list: diff --git a/nanobot/cli/commands.py b/nanobot/cli/commands.py index 1ce2ea057..d072877bf 100644 --- a/nanobot/cli/commands.py +++ b/nanobot/cli/commands.py @@ -1631,7 +1631,7 @@ app.add_typer(pairing_app, name="pairing") @pairing_app.command("list") def pairing_list(): """Show pending pairing requests.""" - from nanobot.pairing import list_pending + from nanobot.pairing import format_expiry, list_pending pending = list_pending() if not pending: @@ -1644,11 +1644,8 @@ def pairing_list(): table.add_column("Sender ID", style="yellow") table.add_column("Expires", style="green") - import time - for item in pending: - remaining = int(item.get("expires_at", 0) - time.time()) - expiry = f"{remaining}s" if remaining > 0 else "expired" + expiry = format_expiry(item.get("expires_at", 0)) table.add_row( item["code"], item["channel"], diff --git a/nanobot/pairing/store.py b/nanobot/pairing/store.py index 4e248e261..340a85b3b 100644 --- a/nanobot/pairing/store.py +++ b/nanobot/pairing/store.py @@ -20,6 +20,9 @@ from loguru import logger from nanobot.config.paths import get_data_dir from nanobot.utils.helpers import _write_text_atomic +# threading.Lock is used so store functions remain callable from both sync CLI +# and async channel handlers. At private-assistant scale (small JSON file, +# sub-millisecond operations) the brief block is acceptable. _LOCK = threading.Lock() _ALPHABET = string.ascii_uppercase + string.digits _CODE_LENGTH = 8 # e.g. ABCD-EFGH @@ -251,5 +254,5 @@ def handle_pairing_command(channel: str, subcommand_text: str) -> str: return ( "Unknown pairing command.\n" - "Usage: `/pairing [list|approve |deny |revoke ]`" + "Usage: `/pairing [list|approve |deny |revoke |revoke ]`" )