Documentation
¶
Overview ¶
Package inbox is the one place a turn picks up anything that arrives while it is running: a line you type, a file dropped in the session's inbox directory by "kvit-coder steer", or an event from a background process.
The agent loop drains it once per iteration. There is no file watcher: the drain reads the directory as part of the same call, so the drain is the poll.
Index ¶
- func Deliver(dir string, m Message) (string, error)
- func PendingCount(dir string) int
- type AskOutcome
- type Inbox
- func (i *Inbox) Ask(ctx context.Context, out io.Writer, prompt string, timeout time.Duration) (string, AskOutcome)
- func (i *Inbox) Awaiting() bool
- func (i *Inbox) Dir() string
- func (i *Inbox) Drain() []Message
- func (i *Inbox) PauseMode() bool
- func (i *Inbox) PauseRequested() bool
- func (i *Inbox) Push(m Message)
- func (i *Inbox) RequestPause() bool
- func (i *Inbox) SetAskNotifier(fn func(asking bool))
- func (i *Inbox) SetPauseMode(on bool)
- func (i *Inbox) Signal() <-chan struct{}
- func (i *Inbox) TakePause() bool
- type Kind
- type Message
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func Deliver ¶
Deliver writes a message into another process's inbox directory. It writes under a temporary name and renames into place, so a turn draining the directory never reads a half-written file.
func PendingCount ¶
PendingCount reports how many inbox files are waiting in dir without removing anything, so a watcher sitting at a turn boundary can decide whether there is anything worth waking for. It applies the same filter as the drain — directories and dotfiles (including half-written .partial-*) do not count — and missing directory means none.
Types ¶
type AskOutcome ¶
type AskOutcome int
AskOutcome says why Ask returned.
const ( // AskAnswered means someone typed a line. AskAnswered AskOutcome = iota // AskCancelled means the turn was cancelled before anyone answered. AskCancelled // AskTimedOut means the time allowed ran out. AskTimedOut )
type Inbox ¶
type Inbox struct {
// Log, when set, is called for something worth saying out loud: an inbox
// file too large to read, or one that could not be read at all.
Log func(string)
// contains filtered or unexported fields
}
Inbox holds messages pushed in this process and picks up files dropped in its directory by another one.
func New ¶
New creates an inbox backed by dir, which is a session's inbox directory. A empty dir gives an inbox with no directory half, which is what a test or a benchmark run wants.
func (*Inbox) Ask ¶
func (i *Inbox) Ask(ctx context.Context, out io.Writer, prompt string, timeout time.Duration) (string, AskOutcome)
Ask puts a question to whoever is at the terminal and returns the first line typed after it.
Every prompt kvit-coder makes goes through here, because two readers of one terminal is a race for each keystroke and the loser waits forever. There is one reader of stdin in the process — the one filling this inbox — and a prompt claims the next line from it rather than opening the terminal again.
A line typed before the question was asked was not answering it, so it is held aside and put back for the loop to treat as ordinary steering. A timeout of zero or less waits indefinitely.
func (*Inbox) Awaiting ¶
Awaiting reports whether a prompt is waiting for someone to type an answer. The next line typed will be taken as that answer rather than reaching the model as steering.
func (*Inbox) Drain ¶
Drain returns everything waiting and empties the inbox: first what this process pushed, then the files in the directory, oldest name first. A file is read and then unlinked, so nothing is delivered twice.
func (*Inbox) PauseMode ¶
PauseMode reports whether the pause prompt is up. An empty Enter then is a resume answer; otherwise it requests a pause or stays ignored.
func (*Inbox) PauseRequested ¶
PauseRequested peeks at a pending pause without consuming it. The progress watcher uses it to hold dots from the empty Enter until the pause prompt is up; TakePause is what consumes the request at the boundary.
func (*Inbox) RequestPause ¶
RequestPause asks the turn to stop at its next iteration boundary and take steering. It reports whether the request is new, so the reader prints its notice once rather than on every empty Enter. A new request also fires the signal, so a tool blocked in Observe.wait wakes early and parks at the boundary instead of holding the pause for its full max_wait; Ask waiters just see a spurious wakeup and loop, since no line was pushed.
func (*Inbox) SetAskNotifier ¶
SetAskNotifier installs a function called with true when a prompt starts waiting for someone to type an answer and false when the wait ends, however it ends. cmd/kvit-coder uses it to put the terminal's window title into its "waiting on you" state for as long as the wait lasts, since the front end is blocked on the turn and cannot see that it has stopped to ask something.
Only the outermost prompt is reported: the count is what decides, so a prompt nested inside another would not clear the state early.
func (*Inbox) SetPauseMode ¶
SetPauseMode records whether the pause prompt is up waiting for a line.
type Kind ¶
type Kind string
Kind says what a message is, which decides how the loop presents it to the model.
const ( // KindUserLine is something a person said mid-turn. It reaches the model // as a user message. KindUserLine Kind = "user_line" // KindProcessEvent is a background process exiting, or matching a pattern // something asked to be told about. It reaches the model as a reminder // appended to the last tool result. KindProcessEvent Kind = "process_event" // KindNotice is anything else kvit-coder wants the model to know. KindNotice Kind = "notice" )