Repository navigation
infinity-agent-core v0.1.0
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/andagent/vialicense-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 configabout.hbs— plaintext Handlebars templatescripts/generate-third-party.sh— orchestration scriptscripts/license-checker-format.json— tells license-checker to include textTHIRD-PARTY— the generated file
- Rust dependencies via
-
add workspace lints and fix all lint violations
New Features
-
redesign landing page around the runtime, add builder
start()API, and benchmark agent memory scalingRuntime 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 builtLocalAgentSystembefore 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). SamplesVmRSSaftermalloc_trimonce each wave is idle and prints a CSV. Addslibcas 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
MemoryChartcomponent 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
AgentTracecomponent, a transcript-style session log: the agent backgroundscargo 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
sandboxread_filereturns 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)ModelEntrygainssupports_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
ModelEntryinto
run_completion/process_batch(rather than a trait method that re-lists
models each turn): the daemon passescatalog.find(&round_model) .supports_image_inputper round (following mid-session model switches);
the Lambda resolves it once fromlist_models().
Agent core (
infinity-agent-core)HistoryManager::get_history(supports_image_input)replaces image
tool-result content in place withIMAGE_OMITTED_PLACEHOLDERwhen 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)RapToolResultcarries eithertextor structuredcontent
(RapToolResultContent::{Text, Image{data, mediaType}}, base64);textis
now optional. Whencontentis present it supersedestext.- 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
contentwhen
present (images →ToolResultContent::Image), else fall back totext.
Sandbox (
sandbox-core/sandbox-local)read_filedetects images by content (magic bytes, not extension) so
mislabeled/extension-less files are classified correctly; returns a
describing text plus base64 image content withdisplay_as: [image, text-summary]. Tool output modeled as a namedToolOutputstruct.
Clients
- Web (
infinity-ui):MessageItemrenders 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 aread_imageRAP 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.pnggolden.
-
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, andinfinity-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_viewportrenders 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
MessageListalways rendersInputBar; rendersChoicePickerabove it
when a pending choice exists.ChoicePickercallsonFocusInputwhen ArrowDown is pressed at the
last choice, shifting focus to the textarea.- Both components converted to
forwardRefto 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,
emitUserChoiceCompletefor each interrupted ID to dismiss associated
choices.thread_worker: HandleUserChoiceCompletein the display event
forwarder by removing frompending_choicesin the memory store.
- Removed
-
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_errorhelper that enqueues an errorToolResultwith 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 failedresult when asynchronous tool execution returns an error, while retaining the detailed display event and logging delivery failures - Add regressions covering missing
close_threadarguments and generic failed-tool fallback delivery
- Add a shared
-
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_completiononly 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 asThinkingChunks, so clients (web UI, TUI, ACP) saw "thinking"
streaming after theToolCallbut before itsToolResultdisplay 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 thehistory.last()match inhandle_contentwhen
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; onlyFinalstill 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 formatasync_streambodies).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. - the ignored calls' name/argument deltas and the interleaved reasoning were
-
use total_tokens for context usage and compaction trigger
With Bedrock prompt caching,input_tokensonly reflects uncached (new) input
tokens. This caused two bugs:-
Web UI context percentage showed ~1% even at 800k tokens: the
TokenUsageprotocol struct lackedtotal_tokens, so the web UI computed
input_tokens + output_tokenswhich only captured the small uncached
portion. -
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 asspawn_order_overridefor 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_ancestorsnow returnsancestor_prefix_lenas a third
tuple element (how many messages at the front come from ancestor threads).HistoryManagerstores this in aCell<usize>field.safe_spawn_point()subtractsancestor_prefix_lenso the returned index
is relative to the thread's own store.apply_compaction()addsancestor_prefix_lento 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'sup_to_orderpreviously included the trailing
unanswered tool call. Afterapply_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
- thread.rs: Return an error ToolResult instead of panicking when
-
hanging caused by
sh -cintercepting 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]onrig-bedrockblocks 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.mdper crate so cargo-smart-release will generate
and track changelogs.
publish = falseadded 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-iorig-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_completionnow 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 vialet ... else { panic!("bug: pending_items and history out of sync") }instead of silently no-op'ing.
- In
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
ThreadBuilderfor per-thread tools, prompts, and models; - root-based configuration inheritance for child threads;
- direct local
McpToolSetandRapToolSetadapters; - 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 theModelProvider
trait over a Unix domain socket, configured in~/.infinity/providers.json
and managed with newinfinity providerCLI commands. The CLI also gained
a readiness handshake so daemon startup failures surface directly.infinity-agent-core:model_provider::remotesubmoduleNew Unix-socket transport for any
ModelProviderimplementor
(model_provider.rsmoved tomodel_provider/mod.rsto 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/WireStreamItemare serializable mirrors of
rig'sCompletionRequestandRawStreamingChoice. - Server:
serve_provider(provider)binds a fresh temp socket path and
returns(path, server_future)for provider binaries to run. - Client:
RemoteModelProviderimplements 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 binaryServes
BedrockProvider::from_env()viaserve_providerand 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. ProvidersConfigpreserves the JSON document's entry order via custom
serde impls backed by aVec(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_providerlaunches each command (normalPATHlookup; 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.SessionManagerbuilds theModelCatalogfrom
RemoteModelProviders and keeps the child handles alive for the
daemon's lifetime.run_daemon(announce_ready: bool)prints the newDAEMON_READY_LINEto
stdout after all initialization succeeds (passed as true by the
infinity daemonsubcommand; the standalone binary keeps it off).
infinity-agent-cli: provider management + launch supervisioninfinity provider install <id> --crate <name> [--git URL | --path DIR]
— cargo-installs the provider crate (sharing therun_cargo_installTUI
plumbing withrap install) and registers it in providers.json,
replacing existing entries in place to preserve ordering.infinity provider updatere-installs all providers with recorded
sources; fullinfinity updatenow also updates providers.launch_daemonspawns{bin} daemonwith 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
mainreturnsExitCodeand 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: theModelProvidertrait and
ModelEntrysemantics, 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
BedrockProviderin-process. - Protocol: one JSON value per line, framed with tokio-util's
Performance (BREAKING)
-
cut idle-agent memory ~30% — seventy thousand agents in 8 GB
Optimizes per-agent resident memory for theagent_scalebenchmark 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_localper driver)route_loopdrives all thread drivers through oneFuturesUnorderedpool instead of spawning each as its ownLocalSettask. 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 theworkersmap 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::SubscriptionEventpayloads boxed- Boxed
resultandinvocationin the rareSubscriptionEventvariant, shrinkingsize_of::<InfinityMessage>()from 352 → 184 bytes; every stored history message previously paid for the fattest variant inline.Boxis serde-transparent so the persisted format is unchanged. - Added
InfinityMessage::tool_result()helper. (Boxing onlyinvocationwas measured and rejected: the enum grows to 200 bytes becauseSubscriptionEventwith an inline result becomes the largest variant.)
Tool-call dedup derived from history instead of a durable index (BREAKING)
HistoryManagerno longer maintainsprocessed_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_pointuses 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_idsreturns a singleHashSet<String>;add_processed_tool_callsremoved. UpdatedInMemoryStateStore, the daemon'sPersistentStateStore, and the LambdaDynamoDbStateStore(old DynamoDBprocessed_tool_callsattributes are ignored).ThreadStatedrops itsprocessed_tool_call_idsfield entirely — serde ignores unknown fields by default, so snapshots from older versions still deserialize.
Benchmark & docs
agent_scaledrains lifecycle notifications per wave (like a real embedding) so they aren't counted as per-agent memory.- Landing page:
MemoryChartregenerated 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.mdupdated 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 publishedstrkind0.0.1 macro and converts every RAPgroup_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_idon the wire; UUIDs in the daemon, caller-chosen conversation
keys in the Lambda runtime).group_id: ThreadIdonRapInvocation,
RapToolResult,RapUserChoice,RapSubscriptionEvent,
RapViewUpdate,RapOAuth;thread_ancestors: Option<Vec<ThreadId>>;
send_subscription_event/send_view_updatetakeThreadId - sandbox-core/local/remote (via sub-agent):
ThreadIdre-exported from
sandbox-core root;MetadataStore::get/delete(&ThreadId);
SandboxBackend::push_sandbox/cleanup_sandbox_permanentlytyped;
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
ToolContextstrings
intoThreadIdwhen building invocations (context types themselves are
Stage 2) - infinity-rap-bridge: callback conversion unwraps
ThreadIdinto
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
Removerigfrom the core provider/agent stack.infinity-provider-protocolnow owns a
minimal model API; the Bedrock provider talks to the official AWS SDK directly
(eliminating the maintainedrig-bedrockpatch); 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 sharedinfinity_agent_core::systemengine, 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 coreDisplayEvent) in favor ofAgentSystem::stepand observer-based local execution. Threadis internal andAgentSystemis notClone.ThreadObserverreplaces inline daemon persistence/display hooks; it has noon_commit.EventCollector::takereturns(thread_id, event)pairs.ConversationStoregainsthread_exists, which checks exact root or child records without creating them.StateStoregains the providedis_thread_stoppedpolicy hook. User text may resume stopped threads; event-style input may not.- Builder tools are stored as
Rc;Toolgains defaultedis_passive. ToolContextand its builder loseinput_queue_arn.- Resident runtime types such as
RunningSystem,SubscribeHandle,ChannelSender, andChannelSendErrorlive undersystem::local. - The daemon no longer exports its old worker/loop/session implementation modules, sleep/RAP tool wrappers, or
boot_rap_servers. SessionManager::send_inputno longer accepts a thread ID oruser_drivenflag and performs no status admission; router admission and lifecycle events own those concerns.rap_callback::start_callback_serveris replaced byinfinity_daemon::launch_session_manager; callback serving accepts aRapCallbackBridge.SessionManager::switch_modelaccepts the requester's sender and returnsResult<(), String>;SharedSessionManagerisRc.- CLI
DisplayEventlives ininfinity_agent_cli::display.
- Removes
-
extract provider protocol into
infinity-provider-protocolcrate- Move
infinity_agent_core::model_provider(theModelProvidertrait,ModelEntry,
erase_streaming_response,SingleModelProvider, and theremoteUnix-socket wire
protocol) into a new lightweightinfinity-provider-protocolcrate whose dependency
surface is just rig-core + serde/schemars + async plumbing — no rap-client,
rap-protocol, rhai, chrono, etc. infinity-provider-bedrocknow depends only oninfinity-provider-protocol, dropping
the entireinfinity-agent-core/rap dependency tree from provider builds.- No legacy path: all consumers (
infinity-agent-coreinternals,infinity-daemon,
infinity-agent-clitests) importinfinity_provider_protocol::…directly instead
of going through a re-export. - Trim now-unused deps from
infinity-agent-core(schemars,tokio-util, tokionet
feature). - Update
docs/docs/infinity-runtime/model-providers.mdto reference the new crate. - Regenerate the Rust section of
THIRD-PARTY(addsinfinity-provider-protocolto the
Apache-2.0 "used by" list); the npm sections are unchanged since no npm deps changed.
- Move
-
make model providers extensible via a dyn-compatible
ModelProvidertraitinfinity-agent-core- New
model_providermodule: ModelProvidertrait (viaasync_trait) exposinglist_models()andinvoke_model(model_id, CompletionRequest), which returns the sameStreamingCompletionResponseas rig'sCompletionModel::streamwith the provider-specific final response erased toProviderStreamingResponse(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 aSingleModelProvider<M>adapter wrapping any rigCompletionModel(used by tests).run_completion/process_batchnow take a genericP: ModelProvider + ?Sized+ model id instead ofMdl: CompletionModel, and no longer takeadditional_request_params/model_id_override/max_output_tokens— provider implementations handle those internally. Concrete callers (e.g. the Lambda) avoiddynentirely and can specialize.
infinity-provider-bedrock(new crate)BedrockProviderwraps the rig-bedrock client and handles all Bedrock-specific request parameters internally: adaptive-thinking config, per-modeladditional_model_request_fields(e.g. anthropic beta flags), and max output tokens.- The two opus-4-6 configurations get distinct provider-scoped model ids (
...-v1and...-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. ModelInfogainsprovider_id;CreateSession/SwitchModelcarryModelRef(breaking).
infinity-daemonmodel_picker.rsreplaced bymodels.rswithModelCatalog: providers stored in aHashMapkeyed 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:
ThreadInfogains a non-optionalselected_model: ModelRef, assigned at thread creation (no parent fallback) and backfilled with the default model when loading metadata serialized before this change.thread_workerresolves 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_paramsRwLocks and the deadSession::model_name/context_windowfields.
infinity-agent-lambda- Instantiates the concrete
BedrockProvider(nodyn) 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 asModelRef. Dropped the unusedrig-bedrockdependency. infinity-uiModelInfotype gainsprovider_id.
./check.bashpasses except two pre-existing, unrelatedsandbox-localenvironment-specific test failures. - New
Commit Statistics
- 113 commits contributed to the release.
- 33 commits were understood as conventional.
- 20 unique issues were worked on: #104, #105, #107, #110, #113, #13, #18, #19, #39, #50, #52, #61, #63, #71, #8, #82, #87, #88, #92, #96
Commit Details
view details
- #104
- Redesign landing page around the runtime, add builder
start()API, and benchmark agent memory scaling (b71baa1)
- Redesign landing page around the runtime, add builder
- #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
ModelProvidertrait (b4a31e2)
- Make model providers extensible via a dyn-compatible
- #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-protocolcrate (27b40fe)
- Extract provider protocol into
- #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 -cintercepting 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 updatetools (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)