Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 2 additions & 7 deletions src/toad/command_catalog.py
Original file line number Diff line number Diff line change
@@ -1,21 +1,16 @@
"""One derived command view joins ACP advertisements and local declarations."""

import asyncio
from dataclasses import dataclass
from collections.abc import Awaitable, Sequence
from typing import TYPE_CHECKING

from toad.slash_command import AgentAdvertisedCommand, CommandPresentation, LocalCommand, SlashCommand
from toad.target_commands import TargetContext, ThreadCommand
from toad.target_commands import CommandContextChanged, TargetContext, ThreadCommand

if TYPE_CHECKING:
from toad.widgets.conversation import Conversation


class CommandDiscoveryCancelled(asyncio.CancelledError):
"""The view that requested commands changed before discovery completed."""


@dataclass(frozen=True)
class CommandCatalog:
entries: tuple[CommandPresentation, ...]
Expand Down Expand Up @@ -73,7 +68,7 @@ async def execute(cls, text: str, conversation: Conversation) -> bool:
if command is None:
return False
return await command.parse_arguments(arguments).apply(conversation)
except CommandDiscoveryCancelled:
except CommandContextChanged:
# A recognized UI command must never fall through to agent input
# because its original target disappeared during an awaited read.
conversation.app.notify(
Expand Down
9 changes: 6 additions & 3 deletions src/toad/target_commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from toad.core import session_requests

from abc import ABC, abstractmethod
from asyncio import CancelledError
from dataclasses import dataclass, field
from pathlib import Path
from functools import partial
Expand All @@ -23,6 +24,10 @@
from toad.widgets.conversation import Conversation


class CommandContextChanged(CancelledError):
"""A command's captured target context is no longer current."""


@dataclass(frozen=True)
class TargetContext:
app: ToadApp
Expand Down Expand Up @@ -119,10 +124,8 @@ def parse_arguments(self, arguments: str) -> ContextualCommand:
return self

async def apply(self, conversation: Conversation, ctx: TargetContext) -> bool:
from toad.command_catalog import CommandDiscoveryCancelled

if await conversation.command_target_context() != ctx:
raise CommandDiscoveryCancelled("Command target changed; refresh this view")
raise CommandContextChanged("Command target changed; refresh this view")
if not self.available(ctx):
raise ValueError("Action is no longer available for this target")
self.execute(ctx)
Expand Down
8 changes: 4 additions & 4 deletions src/toad/widgets/conversation.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
from __future__ import annotations

import asyncio
Expand Down Expand Up @@ -54,7 +54,8 @@
UpCursor,
require_blocks,
)
from toad.command_catalog import CommandCatalog, CommandDiscoveryCancelled
from toad.command_catalog import CommandCatalog
from toad.target_commands import CommandContextChanged, TargetContext
from toad.conversation_comms_handlers import ConversationCommsHandlers
from toad.conversation_submission import ConversationSubmissions
from toad.conversation_turn import ConversationTurn, TurnOwner
Expand Down Expand Up @@ -1507,15 +1508,14 @@

async def command_target_context(self):
from toad.screens.main import MainScreen
from toad.target_commands import TargetContext

screen = self.query_ancestor(MainScreen)
nav = screen.navigation_context
comms = await self.app.preparation.run_thread(
lambda: self.app.coordination_access.observation.observed().service
)
if not self.is_attached or screen.navigation_context != nav:
raise CommandDiscoveryCancelled("Command view changed during discovery")
raise CommandContextChanged("Command view changed during discovery")
return screen.thread_identity.command_target(
lambda actor: TargetContext(
self.app, comms, CommandTarget.named(actor), actor, nav.project_path, nav.owner_mode
Expand Down Expand Up @@ -1551,7 +1551,7 @@
or source.navigation_context != navigation
or agent is not self.agent_binding
):
raise CommandDiscoveryCancelled("Command view changed during discovery")
raise CommandContextChanged("Command view changed during discovery")
return catalog

@handles(core_events.LinkContextChanged)
Expand Down
Loading