Packages
fnord
0.9.38
0.9.40
0.9.39
0.9.38
0.9.37
0.9.36
0.9.35
0.9.34
0.9.33
0.9.32
0.9.31
0.9.30
0.9.29
0.9.28
0.9.27
0.9.26
0.9.25
0.9.24
0.9.23
0.9.22
0.9.21
0.9.20
0.9.19
0.9.18
0.9.17
0.9.16
0.9.15
0.9.14
0.9.13
0.9.12
0.9.11
0.9.10
0.9.9
0.9.8
0.9.7
0.9.6
0.9.5
0.9.4
0.9.3
0.9.2
0.9.1
0.9.0
0.8.99
0.8.98
0.8.97
0.8.96
0.8.95
0.8.94
0.8.93
0.8.92
0.8.91
0.8.90
0.8.89
0.8.88
0.8.87
0.8.86
0.8.85
0.8.84
0.8.83
0.8.82
0.8.81
0.8.80
0.8.79
0.8.78
0.8.77
0.8.76
0.8.75
0.8.74
0.8.73
0.8.72
0.8.71
0.8.70
0.8.69
0.8.68
0.8.67
0.8.66
0.8.65
0.8.64
0.8.63
0.8.62
0.8.61
0.8.60
0.8.59
0.8.58
0.8.57
0.8.56
0.8.55
0.8.54
0.8.53
0.8.52
0.8.51
0.8.50
0.8.49
0.8.48
0.8.47
0.8.46
0.8.45
0.8.44
0.8.43
0.8.42
0.8.41
0.8.40
0.8.39
0.8.38
0.8.37
0.8.36
0.8.35
0.8.34
0.8.33
0.8.32
0.8.31
0.8.30
0.8.29
0.8.27
0.8.26
0.8.25
0.8.24
0.8.23
0.8.22
0.8.21
0.8.20
0.8.19
0.8.18
0.8.17
0.8.16
0.8.15
0.8.14
0.8.13
0.8.12
0.8.11
0.8.1
0.8.0
0.7.24
0.7.23
0.7.22
0.7.21
0.7.20
0.7.19
0.7.18
0.7.17
0.7.16
0.7.15
0.7.14
0.7.13
0.7.12
0.7.11
0.7.10
0.7.9
0.7.8
0.7.7
0.7.6
0.7.5
0.7.3
0.7.2
0.7.1
0.7.0
0.6.9
0.6.8
0.6.7
0.6.6
0.6.5
0.6.4
0.6.3
0.6.1
0.6.0
0.5.9
0.5.8
0.5.7
0.5.6
0.5.5
0.5.4
0.5.3
0.5.2
0.5.1
0.5.0
0.4.44
0.4.43
0.4.42
0.4.41
0.4.40
0.4.39
0.4.38
0.4.37
0.4.36
0.4.35
0.4.34
0.4.33
0.4.32
0.4.30
0.4.29
0.4.28
0.4.27
0.4.26
0.4.25
0.4.24
0.4.23
0.4.22
0.4.21
0.4.20
0.4.19
0.4.18
0.4.17
0.4.16
0.4.15
0.4.14
0.4.13
0.4.12
0.4.11
0.4.10
0.4.9
0.4.8
0.4.7
0.4.6
0.4.5
0.4.4
0.4.3
0.4.2
0.4.1
0.4.0
0.3.0
0.2.0
0.1.0
AI code archaeology
Current section
Files
Jump to
Current section
Files
lib/ai/agent/coordinator/interrupts.ex
defmodule AI.Agent.Coordinator.Interrupts do
@moduledoc """
Functions related to the Coordinator's user-interrupt handling behavior.
"""
defstruct listener: nil, pending: []
@type t :: %__MODULE__{
listener: pid | nil,
pending: AI.Util.msg_list()
}
@type coord :: AI.Agent.Coordinator.t()
@spec new(pid | nil) :: t
def new(pid \\ nil), do: %__MODULE__{listener: pid, pending: []}
@spec init(coord) :: coord
def init(%{conversation_pid: convo, interrupts: int} = state) do
# Only start in interactive TTY sessions and only for Coordinator
cond do
Map.get(int, :listener) != nil ->
state
UI.quiet?() ->
state
UI.is_tty?() ->
task_pid = Task.start(fn -> loop(convo, true) end) |> elem(1)
Map.put(state, :interrupts, new(task_pid))
true ->
state
end
end
@spec pending(coord) :: AI.Util.msg_list()
def pending(%{interrupts: %{pending: p}}), do: p
defp loop(convo_pid, show_msg? \\ false) do
if show_msg? do
UI.info("""
Use enter (or ctrl-j) to interrupt and send feedback to the agent.
NOTE: Interrupts do not interrupt in-flight tool calls or completions.
""")
end
case IO.getn(:stdio, "", 1) do
"\n" ->
# If interrupts are blocked (e.g., during finalization), refuse immediately
if Services.Conversation.Interrupts.blocked?(convo_pid) do
conv_id = Services.Conversation.get_id(convo_pid)
UI.warn(
"Finalizing in progress: interrupts cannot be delivered right now.",
"Ongoing tool operations may complete. Use `-f #{conv_id}` to follow this conversation and queue a new question."
)
loop(convo_pid)
else
"What would you like to say? (empty to ignore)"
|> UI.prompt(optional: true, use_notification_timer: false)
|> case do
{:error, _} ->
:ok
nil ->
:ok
msg when is_binary(msg) ->
msg
|> String.trim()
|> case do
"" ->
:ok
msg ->
Services.Conversation.interrupt(convo_pid, msg)
UI.info(
"Interrupt handler",
"Your message has been queued and will be delivered after the on-going API call completes."
)
end
_ ->
:ok
end
loop(convo_pid, true)
end
# Ignore any other input
_ ->
loop(convo_pid)
end
end
end