Skip to content

Latest commit

 

History

History
277 lines (234 loc) · 15.5 KB

ARCHITECTURE.md

File metadata and controls

277 lines (234 loc) · 15.5 KB

Architecture

Chutes Build preserves the proven upstream Rust agent runtime while replacing public identity, authentication, provider integration, and privacy-sensitive cloud behavior.

Runtime layers

  1. Terminal and CLI: command parsing, interactive rendering, sessions, approvals, keyboard input, and the animated Chutes Build welcome identity.
  2. Agent loop: prompt construction, planning, tool dispatch, compaction, goals, and subagent orchestration.
  3. Chutes routing: model selection, live capability lookup, ordered fallback, image-capability delegation, retry classification, and streaming safeguards.
  4. Tools: coding/filesystem tools, MCP, Context7, web search, isolated browser control, and native Chutes media invocation.
  5. Safety and lifecycle: folder trust, permission classification, endpoint policy, bounded tool output, cancellation, and loop-stationarity handling.
  6. Local state: configuration, credentials, sessions, logs, exports, and secret-filtered memories.md maintenance.

Routing behavior

Auto (Chutes Router) is inserted as the first catalog entry and is selected by default when the user has no stored model preference. It sends a native Chutes routing string — default (the pool saved at chutes.ai/app), a strategy alias, or an inline CHUTES_ROUTING_POOL — to the normal inference host; Chutes resolves the pool server-side per request. The legacy local ID model-router is still accepted and mapped to that string. A concrete selection is attempted first, followed by CHUTES_FALLBACK_MODELS, then Auto. When the Auto string is the bare dashboard alias, a live inline pool built from the current catalogue follows it (:latency by default), so accounts without a saved routing pool still serve; if the whole chain fails to resolve, the surfaced error explains how to configure routing. Duplicates are removed. Only transient/model-unavailable failures before stream start can advance the chain. Authentication, permission, invalid-request, and mid-stream errors do not silently switch models.

The chain covers both request paths, which it did not always: it was wired into chat_completion_stream and not into chat_completion, so an interactive turn recovered from 429 Infrastructure is at maximum capacity while compaction and title generation surfaced it raw. Both now share one chain and one FallbackPolicy. Nothing on the non-streaming path can be mid-stream, so the rule that matters — never switch models once bytes have reached the user — holds there by construction, and the streaming path keeps enforcing it explicitly.

Waiting and retrying is a separate layer and stays there. The sampler actor honours the server's Retry-After on a 429 and retries the same model with a cancellable backoff, up to rate_limit_retry_threshold. The client chain does not sleep: a sleep inside chat_completion_stream would be uninterruptible, because run_one_attempt is not wrapped in a select on the cancellation token.

Each fallback attempt is rebuilt from the unchanged logical request after its candidate model is selected. This is important for mixed-family chains: Qwen, Kimi, DeepSeek, GLM, MiniMax, and future generations cannot inherit one another's template switches or scalar reasoning fields.

Vision inputs are checked against the live Chutes model catalog. If the selected model cannot accept images, the request is delegated to a vision-capable Chutes route. Media generation uses the dedicated Chutes media catalog rather than legacy provider tools.

New media generation returns a versioned MediaArtifact through the tool protocol. The model receives a compact path receipt, while ACP and the pager retain kind, MIME type, size, provider/model provenance, cost, and sidecar path. The pager prepares image and poster previews serially in a background worker, only while inference is idle. Images are reduced to a 1280-pixel display bound, individual prepared artifacts are capped at 8 MiB, and the shared CPU-side cache is capped at 16 MiB. Video playback keeps one visible frame and a bounded two-frame decode queue; pausing or dropping playback cancels its single-threaded FFmpeg process. Music and speech use process-owned pause/resume, seek, volume, duration, and lazy waveform state with native-open fallback. Media-only activity uses the low-frequency TUI tick instead of the normal animation tick. Legacy media variants remain readable for session compatibility.

Media HTTP success bodies that are not JSON stream to a temporary file instead of accumulating in memory. The default download limit is 128 MiB with a 512 MiB hard ceiling; error and JSON bodies are capped at 32 MiB. Workspace inputs default to 64 MiB with the same 512 MiB hard ceiling. Final artifact bundles use create-new writes and roll back partial persistence.

Reasoning compatibility

Reasoning behavior is resolved by one registry in chutes-build-core. Exact known model generations map the user-facing mode to their native chat-template control. Explicit capability menus returned by Chutes or supplied in model configuration take precedence. Unknown future generations keep explicit catalog controls but do not inherit guessed behavior from a broad family-name match.

Auto and fixed-reasoning models expose no effort selector. This prevents a model-specific field from constraining the router or being silently ignored by a model whose reasoning mode cannot be changed. See Model reasoning compatibility.

Inference transport

All sampling clients reuse a process-wide HTTP connection pool. TCP no-delay, bounded connect timeouts, HTTP/2 keepalive, and an HTTP/1 fallback are configured centrally. SSE chunks are parsed and forwarded as they arrive; there is no default application-level replay buffer. Fallback is permitted only before a successful stream begins, so a response never changes model mid-stream.

Official Chutes source policy

The main agent and every worker receive the same primary-source rule. Before answering or making implementation claims about Chutes, they consult both chutes.ai/docs and chutes.ai/news. Direct official pages and indexes are preferred over broad web queries; third-party sources are supporting evidence only. If official coverage is absent or unavailable, the agent must label the claim as unverified and separate it from inference. Queries must not contain credentials, private code, prompts, or repository content.

Account usage surface

Account reads start subscription usage, quotas, optional aggregate quota usage, and optional model statistics concurrently. Model statistics are opt-in. If the aggregate quota response is absent or empty, the client reads each quota through the documented per-chute endpoint concurrently and normalizes the results.

The shell preserves every independently enforced usage window and separately selects the highest consumed percentage for warnings and severity. The pager shows the monthly and rolling four-hour percentages in the compact status item when both are available, exposes all windows through /usage, and preserves each window type even when the API omits a reset timestamp.

Workers

The executor owns the main loop and all mutations. Worker subagents can run concurrently in foreground/background modes, wait as a group, and use isolated worktrees. Nesting is bounded to prevent unreviewable recursive swarms.

Privacy boundary

Outbound provider calls are allowlisted. Telemetry and remote error pipelines are compile-time deadened (the external OTEL stream can never activate — no exporter is constructed), traces export locally, the upstream relay defaults to a closed loopback endpoint, and public commands that depended on upstream cloud services are not registered. See PRIVACY.md.

All Chutes endpoint overrides pass a central URL and DNS policy before use. Without an explicit development opt-in, endpoints must be allowlisted HTTPS hosts with no embedded credentials or custom port, and resolved addresses must not be private, loopback, link-local, multicast, documentation, or other special-use ranges. Ambient credentials are attached only when the final URL is still an official credential destination. Context7 applies the same fail-closed shape and never forwards its optional API key to a custom endpoint.

Automatic permission mode has a narrow no-latency prepass for deterministic read-only shell commands. Project-controlled code, builds, mutations, and network-capable actions go through the classifier or normal permission prompt; classifier timeouts, transport failures, and malformed responses do not become implicit approvals or denials.

Runtime resilience

Repeated identical tool-call signatures receive a corrective reminder and are eventually terminated as a silent stationarity outcome. A repeated true no-op uses a shorter threshold. This prevents unbounded token/tool loops without fabricating a final assistant answer.

Remote ACP background terminals poll cumulative output into the same local log contract as local tasks and retain the real remote exit status. Session-creation failures clear loading state and remove orphan entries. Fork/rewind filters dead history before copying and transfers only referenced, regular compaction checkpoint files.

Module ownership

This is a fork, not a rewrite: most of the runtime is upstream grok-build infrastructure, unmodified or lightly adapted. Since 2.0.0 the whole runtime carries chutes-* names — the total rebrand renamed every crate and module (upstream synchronization is now frozen; see docs/upstream-sync.md), so this section documents the intended ownership boundary instead: which layer a change belongs to, not which names are inherited. It's a map of intent, not a strict enforcement boundary; treat crate placement as one useful signal among several, not a guarantee.

Chutes-owned (product identity, provider integration, privacy posture):

  • chutes-build-core — the domain layer for everything Chutes-specific that isn't a user-facing tool: native routing-string composition and legacy-id mapping, live capability catalog + cache, reasoning-compatibility registry, media catalog/invocation client, Context7 client, credential-endpoint trust policy and SSRF-safe DNS resolution, memory secret-filtering, and wellness nudges.
  • chutes-tools::implementations::chutes — the agent-callable tools backed by the above (media generation/discovery, Context7 lookup, OCR transcription, account usage, isolated browser control). Despite living inside the (upstream) generic tool-runtime crate, this module is Chutes-owned; everything else under implementations/ is upstream-style generic tooling (read_file, bash, grep, web_search, LSP, ...).
  • chutes-voice, chutes-models — Chutes-hosted STT integration and the trimmed Chutes default model catalog, respectively.
  • chutes-secrets — upstream-authored, but per this fork's policy it is now the single canonical secret-detection/redaction layer; Chutes-specific call sites (memory persistence, Context7's outbound guard) and upstream call sites (telemetry/log sanitization) both route through it rather than keeping separate detectors.
  • Product-identity slices scattered through otherwise-upstream crates: rebranded welcome screen and API-key entry in chutes-pager/chutes-shell, plus compile-time product policy in chutes-build-core::product that disables remote session sharing/search, workspace exposure, feedback/data-retention controls, trace upload, and automatic updates.

Retained upstream infrastructure (the proven agent runtime this fork builds on, not specific to any provider):

  • chutes-agent — prompt construction, planning, subagent orchestration, goal tracking.
  • chutes-shell, chutes-shell-base — session lifecycle, config resolution/persistence, credential provider, ACP session handling.
  • chutes-sampler, chutes-sampling-types — the HTTP/streaming client and wire types for chat completions (Chutes-specific handling, e.g. legacy auto-id mapping and role-run merging, is a small, explicitly-commented carve-out inside otherwise-generic client code, not a separate Chutes crate).
  • chutes-tool-runtime, chutes-tool-protocol, chutes-tool-types — the tool trait, wire protocol, and shared type definitions any tool (Chutes-owned or not) is built on.
  • chutes-tools (outside implementations::chutes) — the generic tool implementations: filesystem, shell, search, LSP, MCP, skills.
  • chutes-pager, chutes-pager-render — the TUI: rendering, input, scrollback, views. Chutes product identity is layered on top (see above), the rendering engine itself is upstream.
  • chutes-workspace — permission model, sandboxing, worktrees.
  • chutes-auth — OAuth/credential machinery in general; the Chutes issuer configuration is a caller-supplied parameter, not a fork of this crate.
  • Supporting crates upstream factored out after 1.0.0, all retained and rebranded: chutes-active-sessions / chutes-foreign-sessions (session registries), chutes-session-events / chutes-session-search (per-session event log and local FTS search), chutes-dirs/chutes-paths (home-dir resolution), chutes-workspace-daemon / chutes-diag-server (workspace-server process lifecycle and in-guest loopback diagnostics), chutes-bundle (subagent bundle cache), chutes-compaction-transcript (compacted-segment markdown), and chutes-fuzzy-file-search (walk + nucleo matcher). chutes-file-utils's old events module and the shell's active_sessions/storage/search_* modules moved into them.

Where the Chutes layer is wired in

Ownership is not the whole picture: a Chutes-owned tool is only reachable if an upstream file lists it. Two places do, and the 1.0.0 re-base lost both — every implementation survived and not one tool was callable.

  • chutes-agent::config — the toolset presets. A tool absent from the preset the session runs under does not exist as far as the model is concerned. The presets also decide which sessions get it: the read-only explore and plan toolsets carry the documentation lookups and nothing heavier.
  • chutes-agent::builder — ensure_chutes_tools covers agents whose toolset came from a config file rather than a preset, and the lazy pair (take_lazy_chutes_tools / register_lazy_chutes_tools) moves these tools off the per-turn schema list while registering them on the bridge so use_tool can still call them. Both halves or neither: taking them off the list without registering them deletes the feature silently.

config::tests::chutes_ecosystem_tools_are_in_the_default_toolsets and legacy_imagine_tools_are_not_advertised guard this. They exist because a tool's own unit tests pass whether or not anything can reach it — which is how six thousand lines of working code sat unreachable through a green gate.

The same shape caught the docs: chutes-pager::docs::USER_GUIDE lists which guide chapters ship, and the fork's two chapters were on disk, unlisted, for the same reason.

Crates not listed here are unremarkable utility/support code (formatting, markdown rendering, terminal primitives, telemetry plumbing that is disabled by default, etc.) and follow the same rule of thumb: if it encodes a decision specific to Chutes as a provider or to this fork's product identity, it's Chutes-owned; if it would make equal sense in the upstream project, it's retained infrastructure.

The root Cargo.toml is generated and read-only. Dependency ownership belongs to crate manifests or the generator, which keeps the retained workspace shape reviewable against upstream.