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

orcaterm

package
v0.28.0 Latest Latest
Warning

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

Go to latest
Published: Oct 22, 2026 License: MIT Imports: 11 Imported by: 0

Documentation

Overview

Package orcaterm is the terminal half of chat delivery (cli#907, C1): the bounded `orca` runner, the four terminal calls delivery may make, and the screen checks that decide whether a composer may be typed into.

It started as a copy of internal/teamrouter's orca.go and screen.go, corrected for plan r2's rulings rather than moved: the router keeps its own files, frozen, until phase 3 retires it (C6). The corrections are the #906 guards (hadron-server#1940's bounded, fail-closed call; #1976 and #1985's final recap and notice rules) and R2 (any draft that is not our own is held, including the Claude "stale draft" the router typed over).

Nothing here ever returns, logs or reports screen or draft text: verdicts carry a reason code, never the line that decided them.

Index

Constants

View Source
const (
	CallTimeout    = 90 * time.Second
	MinCallTimeout = 75 * time.Second
	// SubmitWait is the --wait-submit observation window, in seconds: long
	// enough for a turn to start, short enough to leave the call its margin.
	SubmitWait = 30
)

CallTimeout bounds one exec (hadron-server#1940: router.ts CALL_TIMEOUT_MS). A hung Orca costs one call, not the loop. It must stay at least MinCallTimeout: the longest legitimate call is a `terminal send --wait-submit`, whose wait stays under it (see SubmitWait).

View Source
const MaxSendTextBytes = 131_072 - 1

MaxSendTextBytes is the longest text Send can carry: it travels as one argv string (orca terminal send has no file or stdin input), and Linux caps one argument at MAX_ARG_STRLEN, 131,072 bytes with its NUL.

Variables

View Source
var ErrOrca = errors.New("orca call failed")

ErrOrca is a failed or unparsable `orca` call. Its text is a code, never Orca's output.

Functions

func Normalize

func Normalize(s string) string

Normalize removes every whitespace character, so a composer's wrapped rendering compares equal to the one-line text that was typed.

func PromptLineIsEmpty

func PromptLineIsEmpty(lines []string, family string) bool

PromptLineIsEmpty is true only on positive proof the agent's prompt holds no typed text (#5473: tui-idle reports idle while a human is typing).

  • claude: the box between the last two column-0 rules is a bare `❯` plus blank lines, and the ONE nonblank line below it is the mode footer. Orca blanks a composer's text in the tail and reports it as the screen's draft, so the box alone is no proof: ReadComposer judges the draft first, and a Claude empty composer rests on Orca reporting none.
  • codex: the last column-0 `›` line shows the idle placeholder and every line below it is footer.

Anything unrecognized is NOT empty.

func ScreenAgentFamily

func ScreenAgentFamily(sc *Screen) string

ScreenAgentFamily is the agent family a rendered screen POSITIVELY shows, or "": a shell, or both families' chrome at once (hold, never guess).

Types

type Client

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

Client makes the four terminal calls through a guarded runner.

func NewClient

func NewClient(run Runner) *Client

NewClient wraps run in Guard.

func (*Client) List

func (c *Client) List(ctx context.Context) ([]Terminal, error)

List is every live terminal, looked up fresh: delivery never caches one.

func (*Client) ReadScreen

func (c *Client) ReadScreen(ctx context.Context, handle string) *Screen

ReadScreen is the terminal's rendered full-frame screen, or nil when unreadable.

func (*Client) Send

func (c *Client) Send(ctx context.Context, handle, text string, o SendOptions) Receipt

Send types text into the terminal and submits it. An observed send (text plus Enter, no interrupt) retries once, with Orca's durable request id, only after an ambiguous transport failure, as Orca prescribes: the id is bound to the payload and the terminal process, so a retry re-issues, never re-types. Nothing else ever retries a send.

type Composer

type Composer string

Composer is the verdict on the composer: may it be typed into? The non-empty values are S5's hold reasons (hadron-server#2021) where one applies.

const (
	// ComposerEmpty: positively proven empty. The only state typed into.
	ComposerEmpty Composer = "empty"
	// ComposerOwn: it holds text that starts with our prefix, our own
	// leftover from an earlier send. The caller decides recovery.
	ComposerOwn Composer = "own-leftover"
	// ComposerDraft: it holds anything else, a human's text included. R2:
	// held, never cleared, never stored.
	ComposerDraft Composer = "draft"
	// ComposerQuestion: a question, menu or approval prompt has the screen.
	ComposerQuestion Composer = "question"
	// ComposerUnknown: no recognized composer at all, a shell included.
	ComposerUnknown Composer = "screen-unknown"
)

func ReadComposer

func ReadComposer(sc *Screen, family string, ownPrefixes ...string) (state Composer, leftover string)

ReadComposer judges the composer under R2. leftover is the composer's own text, returned only with ComposerOwn so the caller can compare it with what it is about to send; it must never be logged or reported. Text that opens with any of ownPrefixes is the deliverer's own.

Order matters. A draft is judged first and for BOTH families: the router let a Claude screen that proves the composer empty refute Orca's draft (#6736), and R2 inverts that. Any draft that is not ours holds, so a ghost draft now reaches a human through S5's stuck alarm instead of being typed over.

type Liveness

type Liveness string

Liveness is what the window above the prompt proves.

const (
	Busy    Liveness = "busy"
	Idle    Liveness = "idle"
	Unknown Liveness = "unknown"
)

func IdleProof

func IdleProof(lines []string, family string) (Liveness, string)

IdleProof judges the window directly above the prompt chrome: Busy (a running-turn line), Idle (the newest line that is neither weak chrome nor a completed turn's recap/notice tail is a strong idle marker), or Unknown. The reason is a code: the line that decided is never returned.

type Receipt

type Receipt struct {
	// Accepted is Orca's input acceptance.
	Accepted bool
	// TurnStarted is the strong delivery evidence: the agent began a turn.
	TurnStarted bool
	Stages      []string
	// ErrCode is Orca's error code when not accepted.
	ErrCode string
}

Receipt is what a send established. It carries Orca's stages and an error code, never the text sent.

type Result

type Result struct {
	Code   int
	Stdout string
	Stderr string
}

Result is one `orca` invocation's outcome.

type Runner

type Runner func(ctx context.Context, argv []string) Result

Runner execs the `orca` CLI. argv starts at the verb ("terminal", "list", …): the binary is the runner's business, so a fake never sees it.

func Exec

func Exec(bin string, timeout time.Duration) Runner

Exec is the production runner. A call is killed with SIGKILL at the bound (exec.CommandContext's default Cancel is Process.Kill), and ANY failure other than an ordinary non-zero exit — the bound, a missing binary, a pipe error — reads as code 1 with EMPTY stdout: output a failure invalidated must never reach a caller that acts on it (#1940 cycles 1 and 2).

func Guard

func Guard(run Runner) Runner

Guard refuses, without running it, any call outside the four delivery makes. The refusal is the fail-closed shape, so every caller already handles it.

type Screen

type Screen struct {
	Source string
	Tail   []string
	Draft  string
}

Screen is one `orca terminal read --screen` result: the rendered tail, the optional draft field, and the source (only "screen" is a full frame). Draft is held only to be tested; it is never logged or reported.

type SendOptions

type SendOptions struct {
	// Interrupt sends as interrupt-style input. Only a batch that says so
	// (a halt, S6) sets it.
	Interrupt bool
	// EnterOnly submits what the composer already holds: no text is typed.
	EnterOnly bool
	// NoEnter types text without submitting it: a family's clearing
	// keystroke, never a message.
	NoEnter bool
}

SendOptions shape one send.

type Terminal

type Terminal struct {
	Handle        string  `json:"handle"`
	WorktreePath  string  `json:"worktreePath"`
	AgentIdentity *string `json:"agentIdentity"`
	Connected     bool    `json:"connected"`
	Writable      bool    `json:"writable"`
}

Terminal is one `orca terminal list` row.

func (Terminal) Identity

func (t Terminal) Identity() string

Identity is the agent Orca says runs in the terminal, or "".

func (*Terminal) UnmarshalJSON

func (t *Terminal) UnmarshalJSON(b []byte) error

UnmarshalJSON reads Orca's row with router.ts's defaults: connected and writable count unless explicitly false.

Jump to

Keyboard shortcuts

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