diff --git a/src/agent_comms/input_attempt.py b/src/agent_comms/input_attempt.py index d2b5ea8c03..44177f2f07 100644 --- a/src/agent_comms/input_attempt.py +++ b/src/agent_comms/input_attempt.py @@ -206,6 +206,10 @@ class UnresolvedInput: tuple[str, ...], WireName("reviewedForGoals"), WireOmitDefault() ] = () + def record_text(self) -> str: + sequence = self.sequence if self.sequence is not None else "direct input" + return f"Sequence: {sequence} ยท Target: {self.target}\nInput: {self.input_id}\n{self.text}" + @classmethod def of(cls, row: StoredInput) -> UnresolvedInput: return UnresolvedInput( @@ -222,6 +226,9 @@ class HistoricalInput(UnresolvedInput): kw_only=True ) + def record_text(self) -> str: + return super().record_text() + ("\nNotice cleared." if self.notice_dismissed else "") + @classmethod def notice(cls, row: StoredInput, notice_dismissed: bool) -> HistoricalInput: return HistoricalInput( diff --git a/src/agent_comms/ui_model/delivery.py b/src/agent_comms/ui_model/delivery.py index 1ccf03c551..2a0f28e5db 100644 --- a/src/agent_comms/ui_model/delivery.py +++ b/src/agent_comms/ui_model/delivery.py @@ -8,6 +8,7 @@ from agent_comms.declared_family import DeclaredFamily from agent_comms.field_codec import FieldCodec +from agent_comms.input_attempt import HistoricalInput, UnresolvedInput from agent_comms.input_disposition import DeliveryOverview from agent_comms.thread_presentation import ThreadPresentation, ThreadPresentationStatus @@ -43,10 +44,10 @@ class InputDelivery: unconfirmed. Rows are the owner's public input records. """ - inputs: tuple[dict[str, JsonValue], ...] = () + inputs: tuple[UnresolvedInput, ...] = () historical_count: int = 0 dismissed_historical_count: int = 0 - historical_inputs: tuple[dict[str, JsonValue], ...] = () + historical_inputs: tuple[HistoricalInput, ...] = () owner_queue: bool = False read_state: InputDeliveryReadState = LoadedInputDeliveryReadState() @@ -66,14 +67,11 @@ def presentation(self, ordinary: ThreadPresentation) -> ThreadPresentation: @classmethod def from_wire(cls, overview: Mapping[str, JsonValue]) -> InputDelivery: decoded = FieldCodec.decode(DeliveryOverview, overview) - # Toad's current presentation API still consumes wire rows. The paired - # consumer migration follows the stabilization install; it must not be - # changed independently of that client. return cls( - tuple(FieldCodec.encode(row) for row in decoded.inputs), + decoded.inputs, decoded.historical_count, decoded.dismissed_historical_count, - tuple(FieldCodec.encode(row) for row in decoded.historical_inputs), + decoded.historical_inputs, decoded.current_scope.owner_queue, ) diff --git a/tests/test_boundary_decoding.py b/tests/test_boundary_decoding.py index 28c150f3ac..8464834c19 100644 --- a/tests/test_boundary_decoding.py +++ b/tests/test_boundary_decoding.py @@ -28,15 +28,15 @@ def test_coding_socket_record_keeps_object_arguments_and_strict_fields(): FieldCodec.decode(CodingToolRequest, {**payload, 'unexpected': True}) -def test_delivery_uses_declared_overview_and_preserves_current_toad_rows(): +def test_delivery_uses_declared_overview_and_preserves_typed_rows(): current = UnresolvedInput(4, 'worker', 'retained input', 'input-1', 'unknown') historical = HistoricalInput(3, 'worker', 'earlier input', 'input-0', 'unknown', notice_dismissed=True) overview = DeliveryOverview((current,), 1, 2, (historical,), OwnerQueueInputDeliveryScope) wire = FieldCodec.encode(overview) delivery = InputDelivery.from_wire(wire) - assert delivery.inputs == (FieldCodec.encode(current),) - assert delivery.historical_inputs == (FieldCodec.encode(historical),) + assert delivery.inputs == (current,) + assert delivery.historical_inputs == (historical,) assert (delivery.historical_count, delivery.dismissed_historical_count) == (1, 2) assert delivery.owner_queue and delivery.pending == 'awaiting start' assert InputDelivery.from_wire(FieldCodec.encode( diff --git a/tests/test_thread_presentation.py b/tests/test_thread_presentation.py index a52a53a0bb..130b809643 100644 --- a/tests/test_thread_presentation.py +++ b/tests/test_thread_presentation.py @@ -6,6 +6,7 @@ from agent_comms.errors import RouteChangedError, UnregisteredThreadError from agent_comms.field_codec import FieldCodec +from agent_comms.input_attempt import UnresolvedInput from agent_comms.notification_progress import SendingProgress from agent_comms.thread_presentation import ( IdleThreadPresentationStatus, NoThreadPresentation, OtherRouteThread, SessionDisconnected, @@ -105,7 +106,9 @@ def test_idle_attention_is_carried_once_through_idle_presentation(replacement): def test_input_delivery_keeps_its_explanation_and_original_work(working): status = WorkingThreadPresentationStatus() if working else IdleThreadPresentationStatus() ordinary = status.activity_presentation("worker", "thinking", "Actual work") - delivery = InputDelivery(inputs=({"input_id": "unconfirmed"},)) + delivery = InputDelivery(inputs=(UnresolvedInput( + sequence=None, target="worker", text="Unconfirmed input", input_id="unconfirmed", status="unknown", + ),)) shown = delivery.presentation(ordinary) assert shown.status.details == (delivery.summary,) assert shown.summary == (ordinary.summary if working else delivery.summary)