askuser

package
v0.9.5 Latest Latest
Warning

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

Go to latest
Published: May 11, 2026 License: MIT Imports: 6 Imported by: 0

Documentation

Overview

Package askuser holds the daemon-side coordinator for the ask_user MCP tool. The agent (claude / codex / gemini) calls `ask_user` over MCP; the handler registers a pending question + broadcasts SSE; the user answers in the web UI; the answer resolves the pending channel, and the MCP tool returns to the agent.

Lifecycle parallels gate.ApprovalManager but the trigger is an MCP RPC instead of a unix socket dial — there is no subprocess hook, just a blocking goroutine inside the wick process.

Index

Constants

View Source
const DefaultTimeout = 5 * time.Minute

DefaultTimeout is how long Ask blocks before returning an error to the calling agent. Five minutes is long enough for "the user stepped away to grab coffee" but short enough that a forgotten session doesn't pin a goroutine forever. Configurable per Ask via Question.Timeout.

Variables

This section is empty.

Functions

This section is empty.

Types

type Answer

type Answer struct {
	Value string `json:"value,omitempty"`
	Text  string `json:"text,omitempty"`
}

Answer is what the user posts via /sessions/{id}/answer. One of Value / Text is set; if both are present, Value wins (it's the label of a clicked option, more authoritative than free-typed text on the same form).

type AskRequest

type AskRequest struct {
	ID            string   `json:"id"`
	SessionID     string   `json:"session_id"`
	AgentName     string   `json:"agent_name,omitempty"`
	Question      string   `json:"question"`
	Options       []Option `json:"options,omitempty"`
	AllowFreeform bool     `json:"allow_freeform,omitempty"`
}

AskRequest is the broadcast payload — Question + a server-minted id used by both UI (in POST /answer) and the manager's pending map (to route the answer back).

type Manager

type Manager struct {
	// contains filtered or unexported fields
}

Manager owns the per-process pending-asks map. Concurrent-safe; caller is expected to wire OnRequest/OnResolved into the SSE broadcaster so the UI sees state changes.

func NewManager

func NewManager(opt Options) *Manager

NewManager constructs an empty manager.

func (*Manager) Ask

func (m *Manager) Ask(q Question, done <-chan struct{}) (Answer, error)

Ask registers a pending question, fires onRequest, and blocks until the user answers, the timeout fires, or done is closed.

Returns Answer + error. error is non-nil only on timeout / cancel — agent-side handlers convert that into a tool error so the LLM can decide to retry or give up.

func (*Manager) PendingFor

func (m *Manager) PendingFor(sessionID string) []AskRequest

PendingFor returns a snapshot of in-flight asks for one session. Used by the UI for reconnect rehydrate.

func (*Manager) Resolve

func (m *Manager) Resolve(requestID string, ans Answer) bool

Resolve delivers an answer to the matching pending Ask. Returns false if the id is unknown — typical when the agent gave up or the request already timed out.

type Option

type Option struct {
	Label string `json:"label"`
	Value string `json:"value"`
}

Option is one choice presented to the user. Label = what they see, Value = what gets returned to the agent.

type Options

type Options struct {
	DefaultTimeout time.Duration
	OnRequest      func(AskRequest)
	OnResolved     func(sessionID, requestID string)
}

Options wires callbacks. Both are optional; nil = no broadcast.

type Question

type Question struct {
	SessionID     string        `json:"session_id"`
	AgentName     string        `json:"agent_name,omitempty"`
	Question      string        `json:"question"`
	Options       []Option      `json:"options,omitempty"`
	AllowFreeform bool          `json:"allow_freeform,omitempty"`
	Timeout       time.Duration `json:"-"`
}

Question is the input to Manager.Ask. Mirrors the MCP tool's input schema 1:1 so the handler can pass it through unchanged.

Jump to

Keyboard shortcuts

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