inbox

package
v0.6.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 8, 2026 License: MIT Imports: 10 Imported by: 0

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

Constants

This section is empty.

Variables

This section is empty.

Functions

func Deliver

func Deliver(dir string, m Message) (string, error)

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

func PendingCount(dir string) int

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

func New(dir string) *Inbox

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

func (i *Inbox) Awaiting() bool

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) Dir

func (i *Inbox) Dir() string

Dir is the directory this inbox picks files up from.

func (*Inbox) Drain

func (i *Inbox) Drain() []Message

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

func (i *Inbox) PauseMode() bool

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

func (i *Inbox) PauseRequested() bool

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) Push

func (i *Inbox) Push(m Message)

Push adds a message from this process. It never blocks.

func (*Inbox) RequestPause

func (i *Inbox) RequestPause() bool

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

func (i *Inbox) SetAskNotifier(fn func(asking bool))

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

func (i *Inbox) SetPauseMode(on bool)

SetPauseMode records whether the pause prompt is up waiting for a line.

func (*Inbox) Signal

func (i *Inbox) Signal() <-chan struct{}

Signal fires when something is pushed into this process's queue. A blocking tool selects on it to wake early. It says nothing about files dropped in the directory, which are noticed by the next Drain.

func (*Inbox) TakePause

func (i *Inbox) TakePause() bool

TakePause consumes a pause request, reporting whether one was pending. The loop calls it at each iteration boundary.

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"
)

type Message

type Message struct {
	Kind Kind      `json:"kind"`
	Text string    `json:"text"`
	At   time.Time `json:"at"`
}

Message is one item waiting for the turn.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL