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 ¶
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 ¶
Base64Len returns the base64-encoded byte length of n raw bytes (4 chars per 3 bytes, padded).
func Downscale ¶
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 ¶
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 ¶
Decision is the full routing result for an input line (inspectable).
func Parse ¶
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 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 )