Documentation
¶
Overview ¶
Package interaction defines the domain-neutral request / reply / result types that flow between scenarios (back-end content providers) and channels (human-facing surfaces) inside the remote middle layer.
All types are JSON-friendly so a future out-of-process plugin transport (HTTP / gRPC) can use the same contract without redesign.
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Interaction ¶
type Interaction struct {
ID string `json:"id"`
Kind Kind `json:"kind"`
Title string `json:"title"`
Body string `json:"body,omitempty"`
Options []Option `json:"options,omitempty"`
Timeout time.Duration `json:"timeout,omitempty"`
Meta map[string]any `json:"meta,omitempty"`
}
Interaction is the domain-neutral request a scenario sends to a human through a Channel. Channels are responsible for rendering it natively (buttons on Telegram, blocks on Slack, plain numbered text fallback).
type Kind ¶
type Kind string
Kind classifies how an interaction should be presented and answered.
const ( // KindNotify is a one-way push (no reply expected). KindNotify Kind = "notify" // KindConfirm is a yes/no/cancel-style approval. KindConfirm Kind = "confirm" // KindChoose is a "pick one of N" selection. KindChoose Kind = "choose" // KindAsk is a free-form text question. KindAsk Kind = "ask" )
type Notification ¶
type Notification struct {
Title string `json:"title,omitempty"`
Body string `json:"body"`
Meta map[string]any `json:"meta,omitempty"`
}
Notification is a one-way push (no reply expected). Title may be empty; channels should default to a sensible header.
type Option ¶
type Option struct {
Value string `json:"value"`
Label string `json:"label"`
// Style is an optional render hint: "default" | "primary" | "danger".
Style string `json:"style,omitempty"`
}
Option is a selectable choice presented to the human.
type Registry ¶
type Registry[T any] struct { // contains filtered or unexported fields }
Registry owns the pending / await / resolve / cache lifecycle for long-running interactions whose result is delivered out-of-band.
The registry is generic over the result type so different scenarios can keep their own native types (e.g. Result for the wait HTTP endpoint; an internal value type for fully in-process flows). Phase 1 uses Registry[Result] for the Claude Code wait endpoint.
Concurrency model:
- Producer side calls Begin (mark inflight) then eventually Resolve.
- Consumer side (long-poll) calls Await to obtain the receive channel and either picks up the cached answer or blocks until Resolve fires.
- Recently-resolved values are cached for answerTTL so a script reconnect that races with the producer still gets a deterministic answer instead of an "unknown id" 404.
func New ¶
New creates a registry. answerTTL controls how long resolved values remain in the cache for late reconnects (default 30s if <=0).
func (*Registry[T]) Await ¶
Await returns the receive channel for id. ok is true if id is inflight or has a cached answer (the caller may safely block on the channel); false means the id is unknown or already evicted and the channel is nil.
If a cached answer exists, the returned channel is buffered and pre-loaded so a select returns immediately.
func (*Registry[T]) Begin ¶
Begin marks id as inflight. Returns true if the caller should run the producer (newly added) or false if id is already inflight or already cached. Idempotent — duplicate triggers reuse the existing producer instead of double-prompting the human.
func (*Registry[T]) Cancel ¶
Cancel removes id from inflight and drops any waiter channel without delivering. Existing select-blocked goroutines fall through to their own ctx / timeout path.
func (*Registry[T]) IsInflight ¶
IsInflight reports whether id has an active producer.
type Reply ¶
type Reply struct {
InteractionID string `json:"interaction_id"`
Status Status `json:"status"`
// Selected carries the chosen Option.Value when applicable.
Selected string `json:"selected,omitempty"`
// FreeText carries free-form replies (KindAsk) or supplementary input.
FreeText string `json:"free_text,omitempty"`
// Meta is scenario-specific extra data the channel surfaced.
Meta map[string]any `json:"meta,omitempty"`
}
Reply is what the human sent back through a Channel.
type Result ¶
type Result struct {
Status Status `json:"status"`
Decision map[string]any `json:"decision,omitempty"`
Reason string `json:"reason,omitempty"`
}
Result is the final outcome a scenario delivers to the long-poll waiter. Decision is scenario-defined (e.g. Claude Code's hookSpecificOutput map).
type Status ¶
type Status string
Status reports how an interaction concluded.
const ( // StatusAnswered means the human selected an option or replied. StatusAnswered Status = "answered" // StatusCancelled means the human explicitly cancelled. StatusCancelled Status = "cancelled" // StatusTimeout means the budget elapsed before any reply. StatusTimeout Status = "timeout" // StatusError means the channel failed to deliver or read a reply. StatusError Status = "error" )