Skip to content

infinity-agent-core v0.1.0

Choose a tag to compare

@hydro-project-bot hydro-project-bot released this 03 Sep 23:09
· 16 commits to main since this release

Chore

  • add automated THIRD-PARTY file generation with license enforcement
    Generates a plaintext THIRD-PARTY attribution file combining:

    • Rust dependencies via cargo-about (grouped by license, full text)
    • npm dependencies from infinity-ui/ and agent/ via license-checker

    Both ecosystems enforce an allowlist of permissive licenses:

    • Rust (about.toml): Apache-2.0, MIT, ISC, BSD-3-Clause, CC0-1.0,
      CDLA-Permissive-2.0, MPL-2.0, Unicode-3.0, Zlib
    • npm (script): MIT, Apache-2.0, ISC, BSD-2-Clause, BSD-3-Clause, 0BSD,
      CC0-1.0, Unlicense, BlueOak-1.0.0, MPL-2.0

    The script fails if any dependency uses a license not in the allowlist.

    New files:

    • about.toml — cargo-about config
    • about.hbs — plaintext Handlebars template
    • scripts/generate-third-party.sh — orchestration script
    • scripts/license-checker-format.json — tells license-checker to include text
    • THIRD-PARTY — the generated file
  • add workspace lints and fix all lint violations

New Features

  • redesign landing page around the runtime, add builder start() API, and benchmark agent memory scaling

    Runtime API (infinity-agent-core)

    • AgentSystemBuilder::start() builds the local system and starts it with the thread-builder API in one call; AgentSystemBuilder::start_with_observer(...) does the same for the custom-observer path. build_local() remains the two-phase form for embeddings that hold the built LocalAgentSystem before running it (as the daemon does).
    • Updated the module example, simple internal call sites, and all agent-systems docs pages to the shorter form.

    Memory-scaling benchmark

    • New crates/infinity-agent-core/examples/agent_scale.rs: launches waves of agents against a self-driving scripted model; every turn is a full runtime round trip (streamed ~1.2 KB completion with a tool call, async tool result through the input queue, follow-up completion). Samples VmRSS after malloc_trim once each wave is idle and prints a CSV. Adds libc as a dev-dependency.
    • Measured (AMD EPYC 9R14, single thread, in-memory stores): 50,000 agents × 20 turns = 2,000,000 completions in 255 s; 7.9 GB RSS, ≈154 KB per idle agent — fifty thousand resident agents fit in a Raspberry Pi 5's memory.

    Landing page redesign

    Rebuilt around the priorities: clean Rust API in the hero, then Scale → Asynchrony → Serverless chapters, with RAP and Infinity Code as separate product sections.

    • Hero: tagline leads with the Rust framework and the measured Raspberry Pi claim; the subline pitches the architecture ("Infinity does for agents what async did for threads: instead of blocking on slow tools, agents run them concurrently, yield while they wait, and cost nothing until the next event arrives"); stock Docusaurus code block shows the quickstart snippet using the new start() API.
    • Scale: new MemoryChart component plots the measured 21-point agents-vs-memory curve (inverted axes: resident agents against RSS) with a Raspberry Pi 5 reference line, per-agent slope annotation, and a methodology caption linking to the benchmark source.
    • Asynchrony: new AgentTrace component, a transcript-style session log: the agent backgrounds cargo test, drafts release notes while the tests run, and goes idle; the run's failure event wakes it to fix and rerun, and exit 0 closes it out. Tagged rows (user / agent / event) without timestamps; explicit idle rows; fixed-height opacity-only reveal with extra-long pauses after idle rows. Copy names the primitives (subscriptions, spawn_thread, sleep) and explains agent systems as actor-like pools that the runtime intelligently schedules like an async executor, so thousands of agents make progress on a few threads.
    • Serverless: keeps the time-slicing animation; copy reads "Because agent turns never block, Infinity is perfect for serverless environments" (one Lambda step per SQS delivery) and "Infinity agents can run forever with near-zero cost", closing on the CDK constructs and laptop-to-cloud portability.
    • RAP / Infinity Code: standalone sections with title + subtitle headers, aligned to the same 1200px content grid as the chapters; all separators span the identical content width with the same color.
    • Footer: reorganized into three highlight columns — Infinity Runtime (Quickstart, Architecture, Deploy on AWS Lambda), Reactive Agent Protocol (What is RAP?, Build a RAP Tool, Specification), and Infinity Code (Get Started, Background Agents, Slack Bot).
    • Style: all copy rewritten in active Hydro-landing register (~two sentences per paragraph, minimal em-dashes); removed the old layer cards, numbered "·" kickers, dotted connectors, alternating flip layout, the Rust-API detail chapter, and the unused SliceDiagram/code panels.
  • multimodal (image) tool results end-to-end, with image display + review fixes
    Models can declare image input support, RAP tools can return images in tool
    results (both as model-facing content and as inline display segments), the
    sandbox read_file returns image files as image attachments, the web UI
    renders them inline, and the terminal falls back gracefully. Includes a
    shared test-server crate and mock-model / sandbox / TUI / web e2e coverage.

    Provider protocol (infinity-provider-protocol)

    • ModelEntry gains supports_image_input: bool (#[serde(default)], so
      the remote provider socket protocol stays backward compatible).
    • Bedrock provider: all Claude models declare supports_image_input: true.
    • The capability is threaded from the resolved ModelEntry into
      run_completion/process_batch (rather than a trait method that re-lists
      models each turn): the daemon passes catalog.find(&round_model) .supports_image_input per round (following mid-session model switches);
      the Lambda resolves it once from list_models().

    Agent core (infinity-agent-core)

    • HistoryManager::get_history(supports_image_input) replaces image
      tool-result content in place with IMAGE_OMITTED_PLACEHOLDER when the
      model can't accept images (no extra allocation pass). Images kept in
      history become visible again after switching to an image-capable model.

    RAP protocol (rap-protocol)

    • RapToolResult carries either text or structured content
      (RapToolResultContent::{Text, Image{data, mediaType}}, base64); text is
      now optional. When content is present it supersedes text.
    • New DisplaySegment::Image(ImageContent { data, mediaType }) for
      human-facing UIs.
    • Spec docs (tool-result.md) and provider docs updated.

    Daemon (infinity-daemon)

    • RAP callbacks build the rig tool result from structured content when
      present (images → ToolResultContent::Image), else fall back to text.

    Sandbox (sandbox-core / sandbox-local)

    • read_file detects images by content (magic bytes, not extension) so
      mislabeled/extension-less files are classified correctly; returns a
      describing text plus base64 image content with display_as: [image, text-summary]. Tool output modeled as a named ToolOutput struct.

    Clients

    • Web (infinity-ui): MessageItem renders images as an inline bordered
      <img> (data: URL, data-testid="tool-result-image").
    • TUI / ACP (infinity-agent-cli): renderers pick the first supported
      display segment; image-only results show ✓ [image — not displayable in terminal], otherwise the text summary.

    Shared test crate (rap-test-servers, unpublished)

    • start_stub_image_server() serves a read_image RAP tool returning a
      fixed indigo PNG; write_rap_config(cwd, port) points sessions at it.
      Dev-dependency of the CLI and daemon e2e suites.

    Tests

    • agent-core: image tool results reach image-capable models and are replaced
      with the placeholder otherwise.
    • daemon: RAP→rig content conversion (fallbacks, media types).
    • provider-protocol: remote transport round-trips supports_image_input.
    • sandbox-local: PNG content + display segments, content-based detection
      (PNG named .txt), text reads unchanged.
    • TUI e2e: image content reaches the model; terminal renders the text
      fallback (insta snapshot).
    • Web e2e: follow-up request carries the base64 image; transcript renders the
      inline <img>; chat-image-result.png golden.
  • add Claude Fable 5 to Bedrock models list

  • add pretty-print display scripts for sleep tools

    • SleepTool: "Sleeping 30s" or "Sleeping 30s: waiting for deploy"
    • SleepUntilTool: "Sleeping until 2025-01-15 09:00 (US/Pacific)"
    • SleepUntilEventOrInputTool: "Sleeping until event or input"

    Added display_script() to all five sleep tool structs across
    infinity-daemon, infinity-agent-lambda, and infinity-agent-core.

  • show choice picker alongside input and cancel choices on tool interruption

    Choice picker coexists with text input

    Previously, a pending user choice replaced the text input entirely in both
    the CLI and web UI. Now the choice picker renders above the input so users
    can still type while a choice is visible.

    CLI

    • Removed UiMode::ChoicePicker; choice state is tracked via
      UiMode::Normal { choice_focused: bool }.
    • draw_viewport renders both choice picker and text input when a choice
      is active (choice above, input below).
    • Arrow-key focus transitions: Down past last choice → input, Up at top
      of input → choice picker. Cursor only shown when input is focused.

    Web UI

    • MessageList always renders InputBar; renders ChoicePicker above it
      when a pending choice exists.
    • ChoicePicker calls onFocusInput when ArrowDown is pressed at the
      last choice, shifting focus to the textarea.
    • Both components converted to forwardRef to support programmatic focus.

    Cancel pending choices on tool call interruption

    When a user sends input while a tool-initiated choice is pending, the tool
    call is interrupted but the choice was left dangling in the UI.

    • batch_processor: After notifying RAP servers of interrupted tool calls,
      emit UserChoiceComplete for each interrupted ID to dismiss associated
      choices.
    • thread_worker: Handle UserChoiceComplete in the display event
      forwarder by removing from pending_choices in the memory store.
  • add UserChoiceComplete daemon-to-client message

  • add remote host migration UI and daemon orchestration

Bug Fixes

  • return tool failures to the agent

    • Add a shared send_tool_error helper that enqueues an error ToolResult with the original tool and call IDs
    • Convert thread-tool argument and relationship validation failures, including missing close_thread.thread_id, from propagated errors or duplicated message construction into queued tool results
    • Make the batch processor enqueue a generic Error: Tool call failed result when asynchronous tool execution returns an error, while retaining the detailed display event and logging delivery failures
    • Add regressions covering missing close_thread arguments and generic failed-tool fallback delivery
  • fix broken tests after merge

  • suppress stream content after the turn's tool call
    Confirmed bug: with Bedrock (Claude with adaptive/interleaved thinking), the model can
    stream concurrent tool calls in a single assistant message, with reasoning
    interleaved between them. run_completion only executes the first tool call
    (the rest are ignored), but it kept forwarding everything streamed after it:

    • the ignored calls' name/argument deltas and the interleaved reasoning were
      yielded as ThinkingChunks, so clients (web UI, TUI, ACP) saw "thinking"
      streaming after the ToolCall but before its ToolResult display event
      (which only arrives in a later round, via the MCP/RAP callback)
    • worse, a trailing reasoning/text block was committed to history after the
      tool call, which broke the history.last() match in handle_content when
      the tool result arrived — the result was silently dropped and the call
      stranded as unanswered

    Fix in run_completion: once the turn's first tool call has been emitted,
    drop all subsequent stream content for that turn (logging it at info level)
    instead of buffering/yielding it; only Final still flows through to flush
    the turn and finish the round. This keeps the executed tool call as the last
    committed history entry and restores the expected client event order:
    ToolCall → ResponseDone → ToolResult → StartOutput → thinking….

    Also removed the now-dead "Ignoring batched tool call" branch and normalized
    the surrounding indentation (rustfmt does not format async_stream bodies).

    Added regression test concurrent_tool_calls_suppress_trailing_stream_content
    that replays the Bedrock chunk sequence (reasoning → tc-1 → tc-2 deltas →
    interleaved reasoning → tc-2) and asserts no display events are emitted after
    the executed call and that history still ends with tc-1.

  • use total_tokens for context usage and compaction trigger
    With Bedrock prompt caching, input_tokens only reflects uncached (new) input
    tokens. This caused two bugs:

    1. Web UI context percentage showed ~1% even at 800k tokens: the
      TokenUsage protocol struct lacked total_tokens, so the web UI computed
      input_tokens + output_tokens which only captured the small uncached
      portion.

    2. Auto-compaction never triggered: the compaction check compared
      input_tokens (uncached only) against 75% of the context window, so it
      never fired when most input was cached.

  • compaction inside child thread no longer panics on indexing
    When compaction triggered inside a child thread, safe_spawn_point() returned
    an in-memory index that included ancestor messages prepended to the history.
    This index was used as spawn_order_override for the compaction grandchild,
    but the child's actual store only contained its own messages — causing a panic:
    "range end index X out of range for slice of length Y".

    The fix:

    • load_history_with_ancestors now returns ancestor_prefix_len as a third
      tuple element (how many messages at the front come from ancestor threads).
    • HistoryManager stores this in a Cell<usize> field.
    • safe_spawn_point() subtracts ancestor_prefix_len so the returned index
      is relative to the thread's own store.
    • apply_compaction() adds ancestor_prefix_len to the split position (since
      ancestors occupy the beginning of in-memory history) and resets it to 0 after
      compaction (ancestors are consumed into the summary).

    A regression test (compaction_inside_child_thread_does_not_panic) reproduces
    the exact panic from issue #31 and uses insta snapshots to verify both the
    compaction child's inherited history and the post-compaction history.

  • prevent compaction from truncating pending tool calls
    When auto-compaction triggers while an async tool call is pending, the
    compaction summary's up_to_order previously included the trailing
    unanswered tool call. After apply_compaction, the tool call was gone
    from history, causing the subsequent tool result to be orphaned (no
    matching tool call in the conversation sent to the LLM).

  • embed subscription invocation inside SubscriptionEvent to prevent duplicate replay entries

  • address review comments for error handling

    • thread.rs: Return an error ToolResult instead of panicking when
      spawn_thread fails in the conversation store, since this is a
      runtime/IO failure, not a logic bug
  • hanging caused by sh -c intercepting SIGINT, improved config error handling
    Fixes a (the?) hanging issue encountered on Ubuntu 24.04

Other

  • set up cargo-smart-release release workflow (mirroring hydro)

    • chore: set up cargo-smart-release release workflow (mirroring hydro)

    Sets up the release tooling for this workspace following the same
    cargo-smart-release setup as hydro-project/hydro (per its RELEASING.md).

    • .github/workflows/release.yml: new manually-dispatched Release workflow,
      adapted from hydro's. Supports major/minor/patch/keep/auto bumps, optional
      pre-release ids, and a dry-run mode (execute unchecked). Uses the
      hydro-project-bot GitHub App token to push past branch protection, and the
      pinned hydro-project fork of cargo-smart-release (rev e6f3368337a0).
    • RELEASING.md: releasing guide adapted from hydro's, including which crates
      are published and why the others are not, plus an addendum explaining why
      [patch.crates-io] on rig-bedrock blocks publishing the bedrock provider.
    • Crate manifests, 14 publishable crates (rap-protocol, rap-client,
      rap-steering-server, rap-github-event-poller, infinity-protocol,
      infinity-provider-protocol, infinity-agent-core, infinity-mcp-bridge,
      infinity-rap-bridge, infinity-daemon, infinity-agent-cli, sandbox-core,
      sandbox-local, sandbox-remote):
    • publish = true, description, documentation (docs.rs), and
      repository = { workspace = true } (new [workspace.package] in the root
      Cargo.toml).
    • version = "^0.1.0" added to all intra-workspace path dependencies
      (including dev-deps between publishable crates), as required for
      publishing.
    • New empty CHANGELOG.md per crate so cargo-smart-release will generate
      and track changelogs.
    • publish = false added to crates that must not be published:
    • rig-mock, rap-test-servers (test-only; left as path-only dev-deps so
      cargo strips them at publish time),
    • rig-bedrock-patched (vendored fork of crates-io rig-bedrock),
    • infinity-provider-bedrock + infinity-agent-lambda (depend on the
      patched rig-bedrock; publishing would silently drop the patches),
    • infinity-slack-bot (deployment artifact).

Performance

  • coalesce consecutive streamed text chunks
    Address PR #50 review: panic on history/pending desync.

    When a model provider streams an assistant response as multiple text
    chunks, each chunk was persisted as its own message row, inflating disk
    usage. handle_completion now merges an incoming text chunk into the
    last pending item when it is already an assistant text message.

    • In try_merge_pending_text, the in-memory history tail must always
      correspond to the last pending item, so a mismatch is now treated as a
      logical bug via let ... else { panic!("bug: pending_items and history out of sync") } instead of silently no-op'ing.

Refactor

  • don't commit turn data to history until the turn is completed.
    There was a bug encountered where after a timeout the bedrock api would reject a retried request because the request did not end in a user message.

    There was already some commit-then-rollback-on-error kind of logic but that's kind of fragile so this revision changes it to buffer up the data and only commit it when the turn completes.

    fix(infinity-agent-core): trim trailing reasoning on abandoned turns

    Addresses PR #63 review: the terminal flush paths (user cancellation,
    retries exhausted) committed the partial turn verbatim, which could leave
    history ending on a reasoning block. The next input is a fresh user turn,
    and user-input-after-reasoning is rejected by some providers. Adds
    flush_turn_trimming_reasoning(), which keeps the visible partial text but
    drops trailing reasoning/empty-text before committing, restoring the
    pre-refactor remove_trailing_reasoning behavior on these paths.

  • introduce InfinityMessage to replace bare rig Message in conversation storage

  • replace all .unwrap() with .expect() and fix clippy warnings

  • add precheck script, lints

  • extract rap-client crate and unify RAP protocol types

    • Unify all duplicate RAP protocol types into rap-protocol crate:
      RapInvocation (3 copies), ToolsetManifest/ToolDef, and callback
      types (RapToolResult, RapSubscriptionEvent, RapUserChoice) with
      new RapOAuth struct and RapCallback tagged enum

    • Create rap-client crate with HttpClient trait, ToolsetCache trait,
      SimpleHttpClient, InMemoryToolsetCache, ToolsetLoader, RapNotifier,
      and a generic callback server accepting an async closure

    • Update infinity-agent-core, infinity-daemon, infinity-agent-lambda,
      and infinity-agent-cli to import directly from rap-client and
      rap-protocol instead of local duplicates

    • Rewrite daemon callback server to wrap rap-client's generic server,
      routing callbacks directly to SessionManager without mpsc indirection

    • Fix Send lifetime error in send_input's async closure

New Features (BREAKING)

  • add high-level agent system API
    Add ergonomic local agent-system APIs on top of the engine extracted in #96:

    • static builder conveniences for tools, prompts, and RAP notification;
    • channel-backed ThreadHandles for sending inputs and streaming events;
    • launcher mode and ThreadBuilder for per-thread tools, prompts, and models;
    • root-based configuration inheritance for child threads;
    • direct local McpToolSet and RapToolSet adapters;
    • usage-oriented high-level and low-level documentation.
  • run model providers as configurable separate processes over Unix sockets
    The Bedrock provider is no longer hardcoded and linked into the daemon.
    Providers now run as standalone processes that serve the ModelProvider
    trait over a Unix domain socket, configured in ~/.infinity/providers.json
    and managed with new infinity provider CLI commands. The CLI also gained
    a readiness handshake so daemon startup failures surface directly.

    infinity-agent-core: model_provider::remote submodule

    New Unix-socket transport for any ModelProvider implementor
    (model_provider.rs moved to model_provider/mod.rs to host it):

    • Protocol: one JSON value per line, framed with tokio-util's
      LinesCodec; one request per connection (concurrent invocations =
      concurrent connections). ProviderRequest::{ListModels, InvokeModel} →
      ProviderResponse::{Models, InvokeStarted, Chunk…, StreamEnd, Error}.
      WireCompletionRequest / WireStreamItem are serializable mirrors of
      rig's CompletionRequest and RawStreamingChoice.
    • Server: serve_provider(provider) binds a fresh temp socket path and
      returns (path, server_future) for provider binaries to run.
    • Client: RemoteModelProvider implements the full trait over the
      socket, including streaming with mid-stream error forwarding.
    • Tests: socket round trip (list + streamed invocation with usage) against
      a mock model; clean failure on a missing socket.

    infinity-provider-bedrock: new binary

    Serves BedrockProvider::from_env() via serve_provider and prints the
    socket path as its only stdout line (logs go to stderr).

    infinity-daemon: config-driven provider registry

    • BREAKING: the daemon no longer links the Bedrock provider. Providers
      come from ~/.infinity/providers.json — a JSON object mapping provider
      id to { "command": [...], "crate_name"?, "git"?, "path"? }. There is no
      implicit default: a missing/empty config is a startup error pointing at
      infinity provider install.
    • ProvidersConfig preserves the JSON document's entry order via custom
      serde impls backed by a Vec (config order = registration order; the
      first model of the first provider is the global default). Duplicate ids,
      empty ids, and empty commands are rejected.
    • models::spawn_provider launches each command (normal PATH lookup; no
      special resolution) with piped stdout, waits (30s timeout) for the socket
      path line, forwards later stdout to the log, and kills the process on
      drop. SessionManager builds the ModelCatalog from
      RemoteModelProviders and keeps the child handles alive for the
      daemon's lifetime.
    • run_daemon(announce_ready: bool) prints the new DAEMON_READY_LINE to
      stdout after all initialization succeeds (passed as true by the
      infinity daemon subcommand; the standalone binary keeps it off).

    infinity-agent-cli: provider management + launch supervision

    • infinity provider install <id> --crate <name> [--git URL | --path DIR]
      — cargo-installs the provider crate (sharing the run_cargo_install TUI
      plumbing with rap install) and registers it in providers.json,
      replacing existing entries in place to preserve ordering.
    • infinity provider update re-installs all providers with recorded
      sources; full infinity update now also updates providers.
    • launch_daemon spawns {bin} daemon with piped stdout/stderr and races
      (tokio::select!) the ready line against process exit (60s outer
      timeout). If the daemon exits during startup, the CLI reports everything
      it printed to stdout/stderr — previously discarded for the detached
      process. No post-launch connect retries: the socket is bound before
      readiness is announced, so a single connect suffices.
    • CLI main returns ExitCode and prints errors with Display formatting,
      so multi-line failure reports keep real newlines instead of Debug-escaped
      \ns.

    Docs

    • Quickstarts (README, Infinity Code overview, runtime getting-started)
      now include installing the Bedrock provider.
    • New infinity-code/model-providers.md: installing / configuring /
      switching / updating providers, providers.json reference, and
      troubleshooting based on the captured startup output.
    • New infinity-runtime/model-providers.md: the ModelProvider trait and
      ModelEntry semantics, writing a provider, with the Unix-socket process
      transport (stdout contract, line-delimited JSON protocol,
      RemoteModelProvider) covered at the end.

    The lambda crate intentionally keeps linking BedrockProvider in-process.

Performance (BREAKING)

  • cut idle-agent memory ~30% — seventy thousand agents in 8 GB
    Optimizes per-agent resident memory for the agent_scale benchmark added by the parent commit: 154 → 107.4 KB per idle agent (measured slope), with 80,000 agents × 20 turns fitting in 8.89 GB and the 8 GB line crossed at ~71,700 agents. Three independent optimizations:

    Router owns driver futures directly (no spawn_local per driver)

    • route_loop drives all thread drivers through one FuturesUnordered pool instead of spawning each as its own LocalSet task. When a driver goes idle, its future yields its thread ID and the router immediately frees the future and the worker entry (input/subscribe channels).
    • Previously a finished driver's JoinHandle, task allocation, and channel blocks were retained in the workers map until the thread's next message — ~13 KB per idle agent.
    • Panic isolation preserved (each pooled future is wrapped in rap_protocol::log_panic); shutdown wind-down drains the pool.

    InfinityMessage::SubscriptionEvent payloads boxed

    • Boxed result and invocation in the rare SubscriptionEvent variant, shrinking size_of::<InfinityMessage>() from 352 → 184 bytes; every stored history message previously paid for the fattest variant inline. Box is serde-transparent so the persisted format is unchanged.
    • Added InfinityMessage::tool_result() helper. (Boxing only invocation was measured and rejected: the enum grows to 200 bytes because SubscriptionEvent with an inline result becomes the largest variant.)

    Tool-call dedup derived from history instead of a durable index (BREAKING)

    • HistoryManager no longer maintains processed_tool_calls / pending_complete_tool_calls. Incoming tool results are deduplicated by walking the history tail just in time: scan back across trailing tool calls/results (future-proof for concurrent calls, e.g. tc tc tr tr), accept on a matching unanswered call, reject as duplicate on a matching result, discard as stale on any other message (user text, assistant content, subscription events — all turn boundaries, since a subscription event is only injected once pending calls are settled).
    • safe_spawn_point uses the same walk, tracking answered calls during the scan.
    • Durable message-ID dedup is limited to inputs that are not naturally idempotent: user text and subscription events (a redelivered subscription event would mint a fresh injected invocation). Tool results and assistant/tool-call items no longer persist IDs.
    • BREAKING: StateStore::get_processed_ids returns a single HashSet<String>; add_processed_tool_calls removed. Updated InMemoryStateStore, the daemon's PersistentStateStore, and the Lambda DynamoDbStateStore (old DynamoDB processed_tool_calls attributes are ignored). ThreadState drops its processed_tool_call_ids field entirely — serde ignores unknown fields by default, so snapshots from older versions still deserialize.

    Benchmark & docs

    • agent_scale drains lifecycle notifications per wave (like a real embedding) so they aren't counted as per-agent memory.
    • Landing page: MemoryChart regenerated from an 80,000-agent run (8.89 GB total, 108.5 KB/agent); hero, chapter title, and copy updated to "seventy thousand agents on a Raspberry Pi" (measured 8 GB crossing: ~71,700).
    • history-manager.md updated for the new dedup model.

    All workspace tests pass; clippy clean.

Refactor (BREAKING)

  • introduce typed ThreadId for RAP group ids

    • refactor(rap-protocol)!: introduce typed ThreadId for RAP group ids

    Stage 1 of the string-ID to typed-ID migration (#108). Defines ThreadId
    via the published strkind 0.0.1 macro and converts every RAP group_id
    on the wire types, plus all consumers. Serialization is transparent, so
    the wire format and persisted metadata are byte-identical (verified: no
    insta snapshot changes; full test suite passes).

    • rap-protocol: strkind! { pub ThreadId; } with docs (RAP calls it
      group_id on the wire; UUIDs in the daemon, caller-chosen conversation
      keys in the Lambda runtime). group_id: ThreadId on RapInvocation,
      RapToolResult, RapUserChoice, RapSubscriptionEvent,
      RapViewUpdate, RapOAuth; thread_ancestors: Option<Vec<ThreadId>>;
      send_subscription_event/send_view_update take ThreadId
    • sandbox-core/local/remote (via sub-agent): ThreadId re-exported from
      sandbox-core root; MetadataStore::get/delete(&ThreadId);
      SandboxBackend::push_sandbox/cleanup_sandbox_permanently typed;
      RepoState.{group_id, root_thread_id}, CloneRepoArgs.base_thread_id,
      SquashSandboxArgs.from_thread_id, CloneContext.group_id,
      SandboxError::RepoNotFound, and server request payload structs typed
    • infinity-agent-core: rap_tool boundary converts ToolContext strings
      into ThreadId when building invocations (context types themselves are
      Stage 2)
    • infinity-rap-bridge: callback conversion unwraps ThreadId into
      InputMessage.group_id (String until Stage 2)
    • rap-github-event-poller: Subscription.group_id: ThreadId
    • infinity-daemon: view-update routing + test fixture conversions
    • Workspace: strkind = "0.0.1" added to workspace dependencies
  • rig-free provider stack, native Bedrock, minimal deps; refreshed scale claims
    Remove rig from the core provider/agent stack. infinity-provider-protocol now owns a
    minimal model API; the Bedrock provider talks to the official AWS SDK directly
    (eliminating the maintained rig-bedrock patch); rig survives only as an optional
    bridge crate. Re-measured the memory benchmark and refreshed the landing/README claims.

  • extract shared agent system engine
    Extract the daemon's agent execution machinery into a shared infinity_agent_core::system engine, migrate the daemon and Lambda runtimes onto it, and extract the protocol components those embeddings share.

    This PR intentionally stops at the engine and embedding boundary. Static builder conveniences, local MCP/RAP tool-set adapters, ThreadHandle, launcher mode, per-thread launch configuration, and the new usage guides are introduced by #92.

    Breaking changes

    • Removes batch_processor (process_batch, process_input_item, and core DisplayEvent) in favor of AgentSystem::step and observer-based local execution.
    • Thread is internal and AgentSystem is not Clone.
    • ThreadObserver replaces inline daemon persistence/display hooks; it has no on_commit.
    • EventCollector::take returns (thread_id, event) pairs.
    • ConversationStore gains thread_exists, which checks exact root or child records without creating them.
    • StateStore gains the provided is_thread_stopped policy hook. User text may resume stopped threads; event-style input may not.
    • Builder tools are stored as Rc; Tool gains defaulted is_passive.
    • ToolContext and its builder lose input_queue_arn.
    • Resident runtime types such as RunningSystem, SubscribeHandle, ChannelSender, and ChannelSendError live under system::local.
    • The daemon no longer exports its old worker/loop/session implementation modules, sleep/RAP tool wrappers, or boot_rap_servers.
    • SessionManager::send_input no longer accepts a thread ID or user_driven flag and performs no status admission; router admission and lifecycle events own those concerns.
    • rap_callback::start_callback_server is replaced by infinity_daemon::launch_session_manager; callback serving accepts a RapCallbackBridge.
    • SessionManager::switch_model accepts the requester's sender and returns Result<(), String>; SharedSessionManager is Rc.
    • CLI DisplayEvent lives in infinity_agent_cli::display.
  • extract provider protocol into infinity-provider-protocol crate

    • Move infinity_agent_core::model_provider (the ModelProvider trait, ModelEntry,
      erase_streaming_response, SingleModelProvider, and the remote Unix-socket wire
      protocol) into a new lightweight infinity-provider-protocol crate whose dependency
      surface is just rig-core + serde/schemars + async plumbing — no rap-client,
      rap-protocol, rhai, chrono, etc.
    • infinity-provider-bedrock now depends only on infinity-provider-protocol, dropping
      the entire infinity-agent-core/rap dependency tree from provider builds.
    • No legacy path: all consumers (infinity-agent-core internals, infinity-daemon,
      infinity-agent-cli tests) import infinity_provider_protocol::… directly instead
      of going through a re-export.
    • Trim now-unused deps from infinity-agent-core (schemars, tokio-util, tokio net
      feature).
    • Update docs/docs/infinity-runtime/model-providers.md to reference the new crate.
    • Regenerate the Rust section of THIRD-PARTY (adds infinity-provider-protocol to the
      Apache-2.0 "used by" list); the npm sections are unchanged since no npm deps changed.
  • make model providers extensible via a dyn-compatible ModelProvider trait

    infinity-agent-core

    • New model_provider module:
    • ModelProvider trait (via async_trait) exposing list_models() and invoke_model(model_id, CompletionRequest), which returns the same StreamingCompletionResponse as rig's CompletionModel::stream with the provider-specific final response erased to ProviderStreamingResponse (carries token usage only).
    • Providers have no identity of their own — ids are assigned at registration by callers that manage multiple providers.
    • erase_streaming_response() helper and a SingleModelProvider<M> adapter wrapping any rig CompletionModel (used by tests).
    • run_completion/process_batch now take a generic P: ModelProvider + ?Sized + model id instead of Mdl: CompletionModel, and no longer take additional_request_params / model_id_override / max_output_tokens — provider implementations handle those internally. Concrete callers (e.g. the Lambda) avoid dyn entirely and can specialize.

    infinity-provider-bedrock (new crate)

    • BedrockProvider wraps the rig-bedrock client and handles all Bedrock-specific request parameters internally: adaptive-thinking config, per-model additional_model_request_fields (e.g. anthropic beta flags), and max output tokens.
    • The two opus-4-6 configurations get distinct provider-scoped model ids (...-v1 and ...-v1:1m) that both map to the same underlying Bedrock model.

    infinity-protocol

    • New ModelRef { provider_id, model_id } — models are now identified globally by provider id + model id.
    • ModelInfo gains provider_id; CreateSession/SwitchModel carry ModelRef (breaking).

    infinity-daemon

    • model_picker.rs replaced by models.rs with ModelCatalog: providers stored in a HashMap keyed by stable unique non-empty ids (asserted, since the empty id is the metadata serde sentinel); just Bedrock registered for now, but all code paths support multiple providers.
    • Per-thread model selection: ThreadInfo gains a non-optional selected_model: ModelRef, assigned at thread creation (no parent fallback) and backfilled with the default model when loading metadata serialized before this change. thread_worker resolves its thread's model (and context window) from the catalog at startup, falling back to the global default with a warning if the stored model is gone.
    • Fixes the bug where a session created with a specific model reverted to the default after a daemon restart — the selection is now persisted in thread metadata and re-resolved on every worker start.
    • Removed the never-written active_model_id/additional_request_params RwLocks and the dead Session::model_name/context_window fields.

    infinity-agent-lambda

    • Instantiates the concrete BedrockProvider (no dyn) and invokes it with the existing hardcoded model id.

    CLI / web

    • CLI model picker now uses infinity_protocol::ModelInfo (no longer re-exports daemon types); selections are sent as ModelRef. Dropped the unused rig-bedrock dependency.
    • infinity-ui ModelInfo type gains provider_id.

    ./check.bash passes except two pre-existing, unrelated sandbox-local environment-specific test failures.

Commit Statistics

Commit Details

view details
  • #104
    • Redesign landing page around the runtime, add builder start() API, and benchmark agent memory scaling (b71baa1)
  • #105
    • Cut idle-agent memory ~30% — seventy thousand agents in 8 GB (9c07368)
  • #107
    • Set up cargo-smart-release release workflow (mirroring hydro) (ffc27d0)
  • #110
    • Rig-free provider stack, native Bedrock, minimal deps; refreshed scale claims (49ad32e)
  • #113
    • Introduce typed ThreadId for RAP group ids (4b18b37)
  • #13
    • Add Claude Fable 5 to Bedrock models list (66ddd8f)
  • #18
    • Make model providers extensible via a dyn-compatible ModelProvider trait (b4a31e2)
  • #19
    • Run model providers as configurable separate processes over Unix sockets (84f7aff)
  • #39
    • Compaction inside child thread no longer panics on indexing (be6bbd5)
  • #50
    • Coalesce consecutive streamed text chunks (8345b7d)
  • #52
    • Use total_tokens for context usage and compaction trigger (b7a9805)
  • #61
    • Multimodal (image) tool results end-to-end, with image display + review fixes (1935c38)
  • #63
    • Don't commit turn data to history until the turn is completed. (a84b99e)
  • #71
    • Extract provider protocol into infinity-provider-protocol crate (27b40fe)
  • #8
    • Add automated THIRD-PARTY file generation with license enforcement (e2e0719)
  • #82
    • Suppress stream content after the turn's tool call (cd2c5de)
  • #87
    • Fix broken tests after merge (6df9db1)
  • #88
    • Return tool failures to the agent (448dbed)
  • #92
    • Add high-level agent system API (8bef2c5)
  • #96
    • Extract shared agent system engine (9c921fd)
  • Uncategorized
    • Release infinity-provider-protocol v0.1.0, infinity-provider-bedrock v0.1.0, infinity-provider-rig v0.1.0, infinity-agent-core v0.1.0, infinity-mcp-bridge v0.1.0, infinity-rap-bridge v0.1.0, infinity-daemon v0.1.0, infinity-agent-cli v0.1.0, sandbox-core v0.1.0, sandbox-local v0.1.0, sandbox-remote v0.1.0 (7e1cd1d)
    • Release rap-protocol v0.1.0, rap-client v0.1.0, rap-steering-server v0.1.0, rap-github-event-poller v0.1.0, infinity-protocol v0.1.0, infinity-provider-protocol v0.1.0, infinity-provider-bedrock v0.1.0, infinity-provider-rig v0.1.0, infinity-agent-core v0.1.0, infinity-mcp-bridge v0.1.0, infinity-rap-bridge v0.1.0, infinity-daemon v0.1.0, infinity-agent-cli v0.1.0, sandbox-core v0.1.0, sandbox-local v0.1.0, sandbox-remote v0.1.0 (dd8c7f4)
    • Prevent close_thread on root thread and deduplicate get_thread_parent_info calls (bbfb25d)
    • Prevent compaction from truncating pending tool calls (b959506)
    • Add pretty-print display scripts for sleep tools (6abd457)
    • Show choice picker alongside input and cancel choices on tool interruption (59d3314)
    • Embed subscription invocation inside SubscriptionEvent to prevent duplicate replay entries (fe820d8)
    • Introduce InfinityMessage to replace bare rig Message in conversation storage (53e7ef6)
    • Add UserChoiceComplete daemon-to-client message (4169bdc)
    • Add workspace lints and fix all lint violations (b92b7a1)
    • Add remote host migration UI and daemon orchestration (ba10ffd)
    • Address review comments for error handling (5cf4d55)
    • Replace all .unwrap() with .expect() and fix clippy warnings (7634b82)
    • Add precheck script, lints (9757071)
    • Introduce display_as typed variants and use Pierre to display in web client (1e65518)
    • Fix broken spawn_local tests in infinity-agent-core and infinity-daemon (c85fc1a)
    • Move HistoryManager to interior mutability; remove callback_with_history hack; restore subscribe_rx in select (1e92087)
    • Display subthreads in web UI and make it possible to connect to subthreads directly (718509d)
    • Fix streaming cancellation and ensure is_thinking is reset on remove_trailing_reasoning (6b65a9c)
    • Use display_as for tool call pretty-printing in web UI (1e4a489)
    • Extract rap-client crate and unify RAP protocol types (51406e4)
    • Add write:/path permissions, thread_ancestors protocol field, and ancestor-aware grant system (3464fad)
    • Unify all duplicate RAP protocol types into rap-protocol crate (2def5ee)
    • Unify RapInvocation into a single type in rap-protocol (e14509e)
    • Improve handling of API errors that require retrying (3ba5dba)
    • Include thread ID of subscription event child thread reports (7288d8d)
    • Fix manual compaction and add background auto-compaction triggers (9b10a09)
    • Hanging caused by sh -c intercepting SIGINT, improved config error handling (b40442e)
    • Add rig-mock crate and test suite for agent core and daemon (abda067)
    • Further increase timeout for output streaming (6d28500)
    • Add support for UserChoice prompts in RAP protocol and use for permissions expansion in sandbox (b0db6a7)
    • Refactor thread processing loop to improve clarity (1d72b0b)
    • Add rap-github-event-poller crate for local GitHub event polling (783a9ec)
    • Allow auto quit without quit picker when agent is idle (3285dc5)
    • Shift core agent runtime into a daemon with a network protocol for clients (141d697)
    • Add steering file instructions to the default agent prompt (40b1f78)
    • Add support for global RAP config and add rap install / rap update tools (6372cd5)
    • Add displayScript field to RAP tool definitions for pretty-printing tool calls (f7e01f2)
    • Simplify thread report display event name to "Report from child thread {id}" (4e50948)
    • Redesign spinner states (7a8bd6a)
    • Improve spinner display when there is a very large context (2c2cdd6)
    • Implement background compaction using threads (6e7e28b)
    • Add send_message_to_child tool for parent-to-child thread messaging (897b024)
    • Add squash_sandbox tool and base_thread_id to clone_repo (5ffac5f)
    • Add child_of validation to spawn_thread to prevent confused child threads from spawning subthreads (634fba0)
    • Make sure LLM provide streams are always consumed to completion (42dcae7)
    • Add MCP server support to the CLI via in-process RAP proxies (68b4266)
    • Increase stream timeout for fault tolerance (14130f9)
    • Improve error logging for Bedrock provider failures (1221736)
    • Correctly re-load token usage when loading session (d6506f8)
    • Handle retry due to rate limiting (dfc0d89)
    • Fix short circuiting behavior for parallel tool calls (20adff9)
    • Improve stream parsing robustness (2a9d85c)
    • Improve retry logic for stream errors (6a6e1c1)
    • Display in-progress tool call construction as thinking tokens (2ad1422)
    • Improve retry handling and reporting (2c0f58c)
    • Add model provider abstraction and model_id_override support (0effd62)
    • Correctly handle HistoryManager::fork_new for threaded event handling (257c0b8)
    • Extract shared batch processing logic into infinity-agent-core (ec43e34)
    • Fix history management bug with removing trailing empty content (ce9d55b)
    • Add support for interrupting with user input during thinking / output (aa4d560)
    • Add model switcher to CLI with Ctrl+M shortcut (82fbf32)
    • Add tool call and subscription cancellation protocol for resource cleanup (56cfa15)
    • Enforce absolute paths in local sandbox and add CWD to system prompt (c7fa225)
    • Format and update snapshots (772a00c)
    • Implement output streaming for execute_command using RAP subscriptions with debouncing. (b2fb764)
    • Add thinking token visualization to the CLI terminal. (f416795)
    • Improve system prompt to explain threaded code editing flow (aeb483a)
    • Add RAP protocol for notifying tool servers of thread closure (2d60e9d)
    • Remove busted KV caching API use (3367cde)
    • Coalesce multiple incoming events into one LLM invocation (ad2bb1a)
    • Add support for synchronous tool calls that are uninterruptible (544ee9c)
    • Improve thread handling (2f53c50)
    • Redesign synthetic tool call structure to reduce confusion (487589f)
    • Support launching RAP servers as a subprocess (5439050)
    • Store RAP config in a local directory (71c8101)
    • Update model invocations for new API (cee4d10)
    • Context window tracking (1fc5ed8)
    • Initial multi-line input and thinking animation (35b1ab3)
    • Add display_as to RAP tool results (18b60a5)
    • Code editing tools (36c7466)
    • Use Ratatui to render inline viewport (c426a64)
    • Run clippy (ea864bf)
    • Clean up dependencies (fcef65d)
    • Cache jj workspaces for local sandboxes (ba0ba4e)
    • Implement RAP server support in CLI (885c17b)
    • Redesign CLI interface to display calls / subscription events (6c3155a)
    • Reduce scope of message sending trait (8514532)
    • Refactor out side effects of prepare_input and add snapshot tests (86736c1)
    • Restore close_thread tool and tool call logging (7793dab)
    • Remove old implementation (cdcfa16)
    • Stream output of LLM processor instead of accumulating text (8eeaefd)
    • Initial refactor to split out core runtime from Lambda (7242d5c)