Help us shape the future of Go! Take the 2026 Go Developer Survey

question

package
v0.57.0 Latest Latest
Warning

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

Go to latest
Published: Nov 1, 2026 License: AGPL-3.0 Imports: 3 Imported by: 0

Documentation

Overview

Package question carries the ask_user round trip: the agent loop registers a batch of questions it cannot answer on its own, blocks until a client replies, and resumes with the answers as the tool result.

It is the sibling of internal/permission, and deliberately the same shape — a request that is created, announced on the bus, and awaited on a buffered reply channel, so the HTTP handler that answers it runs on a different goroutine from the one blocked on it. The difference is the payload: a permission is answered with a fixed word, a question is answered with free-form structure (selected option labels plus typed text) that only this package knows how to carry.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func AnswersByID added in v0.39.0

func AnswersByID(questions []Question, reply Reply) map[string]Answer

AnswersByID pairs a positional reply with the batch, keyed by question id. A question without an id is left out: nothing can branch on it.

func Visibility added in v0.39.0

func Visibility(questions []Question, answers map[string]Answer) []bool

Visibility reports, for each question in order, whether the user is shown it given the answers to the questions before it. Because a condition only ever names an earlier question, the walk is a single pass — a screen behind a skipped branch is skipped too, since its condition then reads a blank answer.

Types

type Answer

type Answer struct {
	Selected []string `json:"selected,omitempty"`
	Text     string   `json:"text,omitempty"`
}

Answer is the reply to one question. Selected carries the labels chosen from the model's options; Text carries whatever the user typed. Either may be empty: a blank answer is a legitimate non-answer, and what the model should do with it depends on the question — a preference left open is licence to use its own judgement, whereas a question whose content it actually needs was simply not answered.

type Condition added in v0.39.0

type Condition struct {
	// Question is the id of an earlier question in the same batch.
	Question string `json:"question"`
	// Options are option labels of that question. The gated screen is shown
	// when the answer selected any of them; an empty list matches any answer at
	// all (a selection or typed text).
	Options []string `json:"options,omitempty"`
	// Not inverts the match: shown when none of Options was selected.
	Not bool `json:"not,omitempty"`
}

Condition gates a question on an answer already given — the branching that lets one screen decide what the next screen asks. It names an earlier question by id and the option labels that reveal this one.

func (*Condition) Match added in v0.39.0

func (c *Condition) Match(answers map[string]Answer) bool

Match reports whether the condition holds for answers keyed by question id. A nil condition (a screen with no ShowWhen) always matches.

type Manager

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

Manager holds the pending batches. It is safe for concurrent use: the loop goroutine calls Create/Remove while the HTTP handler goroutine calls Reply.

func NewManager

func NewManager() *Manager

func (*Manager) Create

func (m *Manager) Create(req Request) *PendingRequest

func (*Manager) Get

func (m *Manager) Get(id QuestionID) *PendingRequest

func (*Manager) PendingForSession

func (m *Manager) PendingForSession(sessionID string) []Request

PendingForSession returns all unanswered batches for a session. The UI uses this to restore the dialog when the user switches back to a session whose question was hidden by the view change — the agent loop is still blocked. Order is not guaranteed (map iteration).

func (*Manager) Remove

func (m *Manager) Remove(id QuestionID)

Remove discards a pending batch without replying — used when the request is abandoned (e.g. the agent loop was cancelled while waiting for an answer).

func (*Manager) Reply

func (m *Manager) Reply(id QuestionID, reply Reply) bool

Reply answers a pending batch and reports whether it was still pending. The first client to answer wins: the entry is removed before the send, so a second caller finds nothing and gets false.

type Option

type Option struct {
	Label       string `json:"label"`
	Description string `json:"description,omitempty"`
}

Option is one proposed answer the model offered. It is a suggestion, not a constraint: the dialog always carries a free-text field, so the set need not be exhaustive and the user is free to answer in their own words.

type PendingRequest

type PendingRequest struct {
	Request Request
	ReplyCh chan Reply
}

PendingRequest holds a batch awaiting a reply.

type Question

type Question struct {
	ID          string     `json:"id,omitempty"`
	Header      string     `json:"header,omitempty"`
	Question    string     `json:"question"`
	Options     []Option   `json:"options,omitempty"`
	MultiSelect bool       `json:"multiSelect,omitempty"`
	ShowWhen    *Condition `json:"showWhen,omitempty"`
}

Question is one prompt within a batch. Header is the short title shown on the question's own screen; MultiSelect says whether several options may be selected at once (single-select is the default).

ID is a short slug another question can branch on; ShowWhen makes this screen conditional on an earlier answer. A question with no ShowWhen is always shown, so a batch that does not branch is unchanged.

type QuestionID

type QuestionID = id.QuestionID

func NewQuestionID

func NewQuestionID() QuestionID

type Reply

type Reply struct {
	Answers []Answer `json:"answers"`
}

Reply is the reply to a batch: one Answer per question, in the order the questions were asked.

type Request

type Request struct {
	ID        QuestionID `json:"questionId"`
	SessionID string     `json:"sessionId"`
	// OwnerSessionID names the session whose loop is actually blocked when this
	// copy was surfaced under another session — a teammate's batch mirrored to
	// its parent. Empty when the batch is its own session's.
	OwnerSessionID string `json:"ownerSessionId,omitempty"`
	// Teammate is the board handle of the teammate that asked, so the dialog can
	// say whose question the user is answering. It is set on every copy of a
	// teammate's batch — the teammate's own page's copy included — and empty
	// only for a batch the session itself asked, with no teammate involved.
	Teammate  string     `json:"teammate,omitempty"`
	Questions []Question `json:"questions"`
}

Request is a whole batch, as handed to the UI.

Jump to

Keyboard shortcuts

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