input

package
v0.32.0 Latest Latest
Warning

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

Go to latest
Published: Sep 5, 2026 License: MIT Imports: 14 Imported by: 0

Documentation

Overview

Package input routes a raw interactive line — coalesce / steer / queue / interrupt — so the agent collaborates the way people actually type instead of treating every Enter as a new task. Composer text is TEXT: Parse never scans prose for arbitrary file paths (that "helpfully" turned words into attachments because they matched repo paths). The ONE Parse-level extraction is a narrow pasted/dragged ABSOLUTE path to an EXISTING supported image file (the macOS "paste a screenshot" case); every OTHER attachment comes only from an explicit drag/drop or attach event via Resolve. Routing is deterministic and unit-testable; the timing side (coalescing window) lives in the interactive runner.

Index

Constants

View Source
const (
	MaxImageB64Bytes   = 10 << 20 // per-image ceiling, base64-encoded (Anthropic direct; 5MB on Bedrock/Vertex)
	MaxPDFB64Bytes     = 24 << 20 // per-PDF ceiling, base64-encoded — fits the 32MB request envelope with headroom
	MaxRequestB64Bytes = 32 << 20 // whole-request payload ceiling
	MaxAttachments     = 20       // claude.ai's per-message limit; ≤20 also avoids the stricter >20-image 2000px dimension rule

)

Attachment ceilings — the DIRECT Anthropic API limits, verified against the vision + PDF docs at platform.claude.com. These are the binding limits because memcode's gateway talks to the Anthropic API directly AND images always serve on Anthropic (the cheap lane has no vision). On Bedrock/Vertex the per-image cap would be 5MB instead of 10MB — not a path memcode uses, so we don't carry a provider-profile abstraction for it (it would be dead code).

Sizes are checked BASE64-ENCODED, because that's how images travel in the request and base64 inflates raw bytes by ~33% — a raw 8MB image is ~10.7MB on the wire. Checking raw bytes would accept bundles the API then rejects.

Variables

This section is empty.

Functions

func Base64Len

func Base64Len(n int64) int64

Base64Len returns the base64-encoded byte length of n raw bytes (4 chars per 3 bytes, padded).

func Downscale

func Downscale(data []byte, mime string) ([]byte, string)

Downscale shrinks an image's long edge to maxImageEdge (re-encoding in the same format) when it's larger, returning the smaller bytes. It's a best-effort optimization: any decode/encode failure, an already-small image, or a result that isn't actually smaller returns the original bytes + mime unchanged. Only PNG and JPEG are resized — the common screenshot/photo formats; GIF/WebP pass through untouched (no extra decoder dependency, and GIF animation is preserved).

func ImageMatches

func ImageMatches(text, cwd string) []string

ImageMatches returns the substrings of text that are absolute paths to existing supported image/PDF files — the same eligibility criteria used by discoverImagePaths. The TUI uses this to collapse a dragged-in path to a compact "[Image #N]" chip while keeping the exact substring recoverable on submit.

Types

type Attachment

type Attachment struct {
	Path      string `json:"path"`
	Kind      Kind   `json:"kind"`
	Mime      string `json:"mime,omitempty"`
	SHA256    string `json:"sha256,omitempty"`
	SizeBytes int64  `json:"size_bytes,omitempty"`
	Source    string `json:"source"` // drag_drop | paste
}

Attachment is one file referenced in a turn. Raw bytes are NOT stored here; only metadata (and the path, for the runner to read under policy).

func CapAttachments

func CapAttachments(atts []Attachment) (kept []Attachment, dropped int)

CapAttachments enforces the count + aggregate-payload ceilings, measuring each attachment at its BASE64-encoded size (how it travels to the model) so a bundle accepted locally can't blow the request envelope after encoding. Returns the kept attachments (order preserved) and how many were dropped, so the caller can tell the user.

func Resolve

func Resolve(candidate, cwd, source string) (Attachment, bool)

Resolve turns an explicit path (from a drag/drop payload or an attach command, NOT from scanning prose) into an Attachment if it exists. source is recorded for provenance (e.g. "drag_drop", "attach").

PATH POLICY (current behavior, deliberately documented — not yet a hardened gate): a pasted/dragged ABSOLUTE path is honored ANYWHERE on disk (e.g. ~/Desktop, /tmp, /Volumes), because the user explicitly produced the event — but it's narrowed to supported images (imagePathRe) and size-capped. Symlinks are NOT resolved/confined, and an explicit attach of an arbitrary absolute path is not yet confirmation-gated. TODO(attachments): a real policy — workspace-local by default for `attach`, explicit allow for out-of-cwd absolute paths, and no silent symlink traversal outside cwd. TestResolveOutsideCwdAllowed pins TODAY's behavior so any tightening is intentional.

type Bundle

type Bundle struct {
	Text           string       `json:"text"`
	Attachments    []Attachment `json:"attachments"`
	AttachmentOnly bool         `json:"attachment_only,omitempty"`
}

Bundle is one user turn: canonical pasted text plus additive attachments. AttachmentOnly is true only when the routed source consists solely of recognized attachment paths, so callers can distinguish missing chat text from mixed input.

type Decision

type Decision struct {
	Bundle Bundle
	Route  Route
	Reason string
}

Decision is the full routing result for an input line (inspectable).

func Parse

func Parse(line, cwd string) Decision

Parse routes a raw input line. It does NOT scan prose for file paths (that turned words into attachments). The ONE narrow exception is a pasted ABSOLUTE path to an EXISTING image file — the macOS "paste a screenshot" case — which becomes an image attachment so the model can actually see it. Everything else is text.

type Kind

type Kind string

Kind classifies an attachment.

const (
	KindImage     Kind = "image"
	KindPDF       Kind = "pdf" // sent as a native document block — the model reads it on the LLM call
	KindText      Kind = "text"
	KindBinary    Kind = "binary"
	KindDirectory Kind = "directory"
	KindSecret    Kind = "secret" // detected as credential-bearing — never sent raw
)

type Route

type Route string

Route is how an input is folded into the session.

const (
	Coalesce  Route = "coalesce"  // merge into the current turn (timing-decided by the runner)
	Steer     Route = "steer"     // refine the active objective (default)
	Queue     Route = "queue"     // a separate/future task
	Interrupt Route = "interrupt" // stop and re-plan
	Shell     Route = "shell"     // `$` direct-shell lane: run verbatim, no agent/model
)

Jump to

Keyboard shortcuts

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