feat(infinity-agent-core)!: add high-level agent system API - #92
Open
shadaj wants to merge 1 commit into
Open
Conversation
shadaj
added a commit
that referenced
this pull request
Aug 4, 2026
…on + lambda onto it
Introduce `infinity_agent_core::system`, a builder-based, object-oriented API for
running agents, and rebuild both production embeddings on top of it. The docs for
`infinity-runtime` are rewritten around the new API.
Core (`infinity_agent_core::system`):
- `AgentSystemBuilder`: two construction modes — `new(stores, model, sender)` builds a
step-mode `AgentSystem` for serverless platforms; `new_local(stores, model)` builds a
`LocalAgentSystem` with an internal in-process queue (`ChannelSender`).
- `Thread`: loads one conversation thread from the stores; `step(inputs, observer,
cancel_rx)` runs one slice (prepare → completion → history sync → observer commit
barrier → tool dispatch). Per-thread configuration is resolved lazily on first step so
loading a thread never boots tool servers.
- `LocalAgentSystem::start` runs a full actor-system-style runtime: a router plus one
driver task per active thread, with input batching, `<interrupt>` handling for user
text during a completion, deferral of subscription events while a non-passive tool
call is pending, auto-compaction above 75% of the context window, and race-free idle
exit/respawn. `RunningSystem` exposes `sender()`, `send_user_text`, ack'd
`subscribe`, `active_threads`, a `thread_exits` channel for per-conversation resource
teardown, and `begin_shutdown` for process exit. The system itself runs for the
process lifetime.
- `ThreadHandle`: `LocalAgentSystem::start_with_handles()` runs the system with a
built-in channel-based observer (`HandleObserver`) whose subscriber registry is
shared across driver respawns; `RunningSystem::thread_handle(thread_id)` then
attaches to a thread (creating its subscription even before the thread exists) and
returns a handle with `replay()` for the initial snapshot, an unbounded `events`
queue of `AgentEvent`s (exactly-once relative to the replay), and
`send_user_text`/`send` for input.
- `ThreadObserver`: synchronous `on_event(AgentEvent)` fan-out (exactly-once relative
to `on_subscribe` replay snapshots) plus awaited durability hooks
(`on_user_choice_required`/`_dismissed`, `on_commit` before tool dispatch).
- `ModelSource` (per-round model resolution; enables mid-session model switching) with
`StaticModel`; `ThreadConfigSource` (per-thread tools/prompt/notifier; enables
per-session RAP servers) with `StaticThreadConfig`; `DeferQueue` with
`InMemoryDeferQueue`/`NoDeferral`.
- `AgentEvent`/`ReplaySnapshot`/`UserChoice` display types; `Tool::is_passive()`;
in-memory `ConversationStore`/`StateStore` implementations in
`infinity_agent_core::stores`; tokio-backed sleep tools moved into the core
(`with_tokio_sleep_tools`).
- The core API is platform-neutral: `input_queue_arn` is removed from `ToolContext`
and the builder. Platform-specific configuration lives on the tools that need it
(the Lambda sleep tools now carry the input queue ARN as a field, alongside their
scheduler client, role ARN, and delay queue URL).
- All 20 daemon agent-loop tests migrated into core `system/tests.rs` with
byte-identical snapshots, plus 3 new `ThreadHandle` tests (send/receive + late
replay, survival across idle/respawn, dropped-handle pruning).
Daemon (`infinity-daemon`):
- One daemon-lifetime agent system replaces per-session agent loops. Sessions are root
threads; a `thread_exits` watcher marks sessions idle and shuts down their RAP
servers when no keep-alive client is attached.
- New `rap_servers.rs`: `ManagedRapServer`/`ManagedRapTool`/`SessionRapManager` — RAP
servers boot lazily per session via `ThreadConfigSource` and reboot transparently
after idle shutdown. Config-source info messages ("Using local config", etc.) are
preserved at first resolve.
- `DaemonObserver` implements the observer hooks (usage persistence, pending user
choices, live-attach replay including mid-stream thinking).
- `CatalogModelSource` resolves each thread's persisted model selection per round.
- Deleted: `session/thread_worker.rs`, `session/agent_loop.rs`, `sleep_tools.rs`,
`InMemoryMessageSender`, `Session`, `spawn_session`, `AgentMessage`.
Lambda (`infinity-agent-lambda`): event handler rewritten on
`AgentSystemBuilder::new(...).build()` + `thread()`/`filter_deferrable`/`step` with an
`EventCollector`, replacing the hand-rolled `process_batch` plumbing.
Docs (`docs/docs/infinity-runtime`): the single "Rust API" page is replaced by two
sections. "The Agent System API" (overview, building-a-system, running-locally,
step-mode, observers) documents the new high-level API — including thread handles —
and the patterns both embeddings use; "The Low-Level API" (overview, history-manager,
completion-loop) documents the platform traits and loop pieces underneath and when to
use them. Cross-references across overview/architecture/built-in-tools/
deploying-on-lambda updated.
BREAKING CHANGE: `infinity-daemon` no longer exports `sleep_tools`, `AgentMessage`,
`thread_worker`, `agent_loop`, `Session`, or `InMemoryMessageSender`; `SessionManager`'s
API is reshaped around the single agent system and `SharedSessionManager` is now
`Rc<tokio::Mutex<...>>`. In `infinity-agent-core`, builder tools are stored as
`Rc<dyn Tool<M>>`, `Tool` gains a defaulted `is_passive` method, `ToolContext` and
`AgentSystemBuilder` lose `input_queue_arn`, and daemon/lambda display paths now emit
`AgentEvent` instead of `DisplayEvent`.
Co-authored-by: Infinity 🤖 <infinity@hydro.run>
PR: #92
shadaj
force-pushed
the
sandbox-eb0ff46e-96e1-4d2d-8c0b-9c683fa4d1b9
branch
from
August 4, 2026 22:36
5689aff to
7a10b86
Compare
Deploying infinity with
|
| Latest commit: |
eadab61
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://1b2d8fb6.infinity-dc7.pages.dev |
| Branch Preview URL: | https://sandbox-eb0ff46e-96e1-4d2d-8.infinity-dc7.pages.dev |
shadaj
added a commit
that referenced
this pull request
Aug 4, 2026
…on + lambda onto it
Introduce `infinity_agent_core::system`, a builder-based, object-oriented API for
running agents, and rebuild both production embeddings on top of it. The docs for
`infinity-runtime` are rewritten around the new API.
Core (`infinity_agent_core::system`):
- `AgentSystemBuilder`: two construction modes — `new(stores, model, sender)` builds a
step-mode `AgentSystem` for serverless platforms; `new_local(stores, model)` builds a
`LocalAgentSystem` with an internal in-process queue (`ChannelSender`).
- `Thread`: loads one conversation thread from the stores. `step(inputs, observer,
defer, cancel_rx)` runs one slice with the deferral policy applied (filter →
prepare → completion → history sync → observer commit barrier → tool dispatch);
`step_no_defer(batch, observer, cancel_rx)` is the raw step for callers that
compose `filter_deferrable` themselves (the local driver, which must skip no-op
steps when everything was deferred). Per-thread configuration is resolved lazily on
first step so loading a thread never boots tool servers.
- `LocalAgentSystem::start` runs a full actor-system-style runtime: a router plus one
driver task per active thread, with input batching, `<interrupt>` handling for user
text during a completion, deferral of subscription events while a non-passive tool
call is pending, auto-compaction above 75% of the context window, and race-free idle
exit/respawn. `RunningSystem` exposes `sender()`, `send_user_text`, ack'd
`subscribe`, `active_threads`, a `thread_exits` channel for per-conversation resource
teardown, and `begin_shutdown` for process exit. The system itself runs for the
process lifetime.
- `ThreadHandle`: `LocalAgentSystem::start_with_handles()` runs the system with a
built-in channel-based observer (`HandleObserver`) whose subscriber registry is
shared across driver respawns; `RunningSystem::thread_handle(thread_id)` then
attaches to a thread (creating its subscription even before the thread exists) and
returns a handle with `replay()` for the initial snapshot, an unbounded `events`
queue of `AgentEvent`s (exactly-once relative to the replay), and
`send_user_text`/`send` for input.
- `ThreadObserver`: synchronous `on_event(AgentEvent)` fan-out (exactly-once relative
to `on_subscribe` replay snapshots) plus awaited durability hooks
(`on_user_choice_required`/`_dismissed`, `on_commit` before tool dispatch).
- `ModelSource` (per-round model resolution; enables mid-session model switching) with
`StaticModel`; `ThreadConfigSource` (per-thread tools/prompt/notifier; enables
per-session RAP servers) with `StaticThreadConfig`; `DeferQueue` with
`InMemoryDeferQueue`/`NoDeferral`.
- `AgentEvent`/`ReplaySnapshot`/`UserChoice` display types; `Tool::is_passive()`;
in-memory `ConversationStore`/`StateStore` implementations in
`infinity_agent_core::stores`; tokio-backed sleep tools moved into the core
(`with_tokio_sleep_tools`).
- The core API is platform-neutral: `input_queue_arn` is removed from `ToolContext`
and the builder. Platform-specific configuration lives on the tools that need it
(the Lambda sleep tools now carry the input queue ARN as a field, alongside their
scheduler client, role ARN, and delay queue URL).
- All 20 daemon agent-loop tests migrated into core `system/tests.rs` with
byte-identical snapshots, plus 3 new `ThreadHandle` tests (send/receive + late
replay, survival across idle/respawn, dropped-handle pruning).
Daemon (`infinity-daemon`):
- One daemon-lifetime agent system replaces per-session agent loops. Sessions are root
threads; a `thread_exits` watcher marks sessions idle and shuts down their RAP
servers when no keep-alive client is attached.
- New `rap_servers.rs`: `ManagedRapServer`/`ManagedRapTool`/`SessionRapManager` — RAP
servers boot lazily per session via `ThreadConfigSource` and reboot transparently
after idle shutdown. Config-source info messages ("Using local config", etc.) are
preserved at first resolve.
- `DaemonObserver` implements the observer hooks (usage persistence, pending user
choices, live-attach replay including mid-stream thinking).
- `CatalogModelSource` resolves each thread's persisted model selection per round.
- Deleted: `session/thread_worker.rs`, `session/agent_loop.rs`, `sleep_tools.rs`,
`InMemoryMessageSender`, `Session`, `spawn_session`, `AgentMessage`.
Lambda (`infinity-agent-lambda`): event handler rewritten on
`AgentSystemBuilder::new(...).build()` + `thread()` + the deferral-aware
`Thread::step` with an `EventCollector`, replacing the hand-rolled `process_batch`
plumbing.
Docs (`docs/docs/infinity-runtime`): the single "Rust API" page is replaced by two
sections. "The Agent System API" (overview, building-a-system, running-locally,
step-mode, observers) documents the new high-level API — including thread handles and
the step/step_no_defer split — and the patterns both embeddings use; "The Low-Level
API" (overview, history-manager, completion-loop) documents the platform traits and
loop pieces underneath and when to use them. Cross-references across
overview/architecture/built-in-tools/deploying-on-lambda updated.
BREAKING CHANGE: `infinity-daemon` no longer exports `sleep_tools`, `AgentMessage`,
`thread_worker`, `agent_loop`, `Session`, or `InMemoryMessageSender`; `SessionManager`'s
API is reshaped around the single agent system and `SharedSessionManager` is now
`Rc<tokio::Mutex<...>>`. In `infinity-agent-core`, builder tools are stored as
`Rc<dyn Tool<M>>`, `Tool` gains a defaulted `is_passive` method, `ToolContext` and
`AgentSystemBuilder` lose `input_queue_arn`, and daemon/lambda display paths now emit
`AgentEvent` instead of `DisplayEvent`.
Co-authored-by: Infinity 🤖 <infinity@hydro.run>
PR: #92
shadaj
force-pushed
the
sandbox-eb0ff46e-96e1-4d2d-8c0b-9c683fa4d1b9
branch
from
August 4, 2026 23:59
7a10b86 to
32bcb39
Compare
Member
|
Oh god |
shadaj
added a commit
that referenced
this pull request
Aug 5, 2026
…on + lambda onto it
Introduce `infinity_agent_core::system`, a builder-based, object-oriented API for
running agents, and rebuild both production embeddings on top of it. The docs for
`infinity-runtime` are rewritten around the new API.
Core (`infinity_agent_core::system`):
- `AgentSystemBuilder`: two construction modes — `new(stores, model, sender)` builds a
step-mode `AgentSystem` for serverless platforms; `new_local(stores, model)` builds a
`LocalAgentSystem` with an internal in-process queue (`ChannelSender`).
- `Thread`: loads one conversation thread from the stores. `step(inputs, observer,
defer, cancel_rx)` runs one slice with the deferral policy applied (filter →
prepare → completion → history sync → observer commit barrier → tool dispatch);
`step_no_defer(batch, observer, cancel_rx)` is the raw step for callers that
compose `filter_deferrable` themselves (the local driver, which must skip no-op
steps when everything was deferred). Per-thread configuration is resolved lazily on
first step so loading a thread never boots tool servers.
- `LocalAgentSystem::start` runs a full actor-system-style runtime: a router plus one
driver task per active thread, with input batching, `<interrupt>` handling for user
text during a completion, deferral of subscription events while a non-passive tool
call is pending, auto-compaction above 75% of the context window, and race-free idle
exit/respawn. `RunningSystem` exposes `sender()`, `send_user_text`, ack'd
`subscribe`, `active_threads`, a `thread_exits` channel for per-conversation resource
teardown, and `begin_shutdown` for process exit. The system itself runs for the
process lifetime.
- `ThreadHandle`: `LocalAgentSystem::start_with_handles()` runs the system with a
built-in channel-based observer (`HandleObserver`) whose subscriber registry is
shared across driver respawns; `RunningSystem::thread_handle(thread_id)` then
attaches to a thread (creating its subscription even before the thread exists) and
returns a handle with `replay()` for the initial snapshot, an unbounded `events`
queue of `AgentEvent`s (exactly-once relative to the replay), and
`send_user_text`/`send` for input.
- `ThreadObserver`: synchronous `on_event(AgentEvent)` fan-out (exactly-once relative
to `on_subscribe` replay snapshots) plus awaited durability hooks
(`on_user_choice_required`/`_dismissed`, `on_commit` before tool dispatch).
- `ModelSource` (per-round model resolution; enables mid-session model switching) with
`StaticModel`; `ThreadConfigSource` (per-thread tools/prompt/notifier; enables
per-session RAP servers) with `StaticThreadConfig`; `DeferQueue` with
`InMemoryDeferQueue`/`NoDeferral`.
- `AgentEvent`/`ReplaySnapshot`/`UserChoice` display types; `Tool::is_passive()`;
in-memory `ConversationStore`/`StateStore` implementations in
`infinity_agent_core::stores`; tokio-backed sleep tools moved into the core
(`with_tokio_sleep_tools`).
- The core API is platform-neutral: `input_queue_arn` is removed from `ToolContext`
and the builder. Platform-specific configuration lives on the tools that need it
(the Lambda sleep tools now carry the input queue ARN as a field, alongside their
scheduler client, role ARN, and delay queue URL).
- All 20 daemon agent-loop tests migrated into core `system/tests.rs` with
byte-identical snapshots, plus 3 new `ThreadHandle` tests (send/receive + late
replay, survival across idle/respawn, dropped-handle pruning).
Daemon (`infinity-daemon`):
- One daemon-lifetime agent system replaces per-session agent loops. Sessions are root
threads; a `thread_exits` watcher marks sessions idle and shuts down their RAP
servers when no keep-alive client is attached.
- New `rap_servers.rs`: `ManagedRapServer`/`ManagedRapTool`/`SessionRapManager` — RAP
servers boot lazily per session via `ThreadConfigSource` and reboot transparently
after idle shutdown. Config-source info messages ("Using local config", etc.) are
preserved at first resolve.
- `DaemonObserver` implements the observer hooks (usage persistence, pending user
choices, live-attach replay including mid-stream thinking).
- `CatalogModelSource` resolves each thread's persisted model selection per round.
- Fix a pre-existing bug where the requesting client received `ModelSwitched` twice
(once via the subscriber broadcast, once via the direct reply): `switch_model` now
tracks whether the broadcast reached the requester (`same_channel`) and only sends
directly when it did not. Verified by the `switch_model_mid_session` web e2e test.
- The `chat-image-result` web golden screenshot is regenerated: the old golden
captured a layout artifact of the eager RAP-boot flow; the lazy flow renders the
canonical transcript (verified against the DOM).
- Deleted: `session/thread_worker.rs`, `session/agent_loop.rs`, `sleep_tools.rs`,
`InMemoryMessageSender`, `Session`, `spawn_session`, `AgentMessage`.
Lambda (`infinity-agent-lambda`): event handler rewritten on
`AgentSystemBuilder::new(...).build()` + `thread()` + the deferral-aware
`Thread::step` with an `EventCollector`, replacing the hand-rolled `process_batch`
plumbing.
Docs (`docs/docs/infinity-runtime`): the single "Rust API" page is replaced by two
sections. "The Agent System API" (overview, building-a-system, running-locally,
step-mode, observers) documents the new high-level API — including thread handles and
the step/step_no_defer split — and the patterns both embeddings use; "The Low-Level
API" (overview, history-manager, completion-loop) documents the platform traits and
loop pieces underneath and when to use them. Cross-references across
overview/architecture/built-in-tools/deploying-on-lambda updated.
BREAKING CHANGE: `infinity-daemon` no longer exports `sleep_tools`, `AgentMessage`,
`thread_worker`, `agent_loop`, `Session`, or `InMemoryMessageSender`; `SessionManager`'s
API is reshaped around the single agent system (`switch_model` now takes the
requester's sender and returns `Result<(), String>`) and `SharedSessionManager` is now
`Rc<tokio::Mutex<...>>`. In `infinity-agent-core`, builder tools are stored as
`Rc<dyn Tool<M>>`, `Tool` gains a defaulted `is_passive` method, `ToolContext` and
`AgentSystemBuilder` lose `input_queue_arn`, and daemon/lambda display paths now emit
`AgentEvent` instead of `DisplayEvent`.
Co-authored-by: Infinity 🤖 <infinity@hydro.run>
PR: #92
shadaj
force-pushed
the
sandbox-eb0ff46e-96e1-4d2d-8c0b-9c683fa4d1b9
branch
from
August 5, 2026 21:24
32bcb39 to
723ade3
Compare
shadaj
added a commit
that referenced
this pull request
Aug 5, 2026
…on + lambda onto it
Introduce `infinity_agent_core::system`, a builder-based, object-oriented API for
running agents, and rebuild both production embeddings on top of it. The docs for
`infinity-runtime` are rewritten around the new API.
Core (`infinity_agent_core::system`):
- `AgentSystemBuilder`: two construction modes — `new(stores, model, sender)` builds a
step-mode `AgentSystem` for serverless platforms; `new_local(stores, model)` builds a
`LocalAgentSystem` with an internal in-process queue (`ChannelSender`).
- `Thread`: loads one conversation thread from the stores. `step(inputs, observer,
defer, cancel_rx)` runs one slice with the deferral policy applied (filter →
prepare → completion → history sync → observer commit barrier → tool dispatch);
`step_no_defer(batch, observer, cancel_rx)` is the raw step for callers that
compose `filter_deferrable` themselves (the local driver, which must skip no-op
steps when everything was deferred). Per-thread configuration is resolved lazily on
first step so loading a thread never boots tool servers.
- `LocalAgentSystem::start` runs a full actor-system-style runtime: a router plus one
driver task per active thread, with input batching, `<interrupt>` handling for user
text during a completion, deferral of subscription events while a non-passive tool
call is pending, auto-compaction above 75% of the context window, and race-free idle
exit/respawn. `RunningSystem` exposes `sender()`, `send_user_text`, ack'd
`subscribe`, `active_threads`, a `thread_exits` channel for per-conversation resource
teardown, and `begin_shutdown` for process exit. The system itself runs for the
process lifetime.
- `ThreadHandle`: `LocalAgentSystem::start_with_handles()` runs the system with a
built-in channel-based observer (`HandleObserver`) whose subscriber registry is
shared across driver respawns; `RunningSystem::thread_handle(thread_id)` then
attaches to a thread (creating its subscription even before the thread exists) and
returns a handle with `replay()` for the initial snapshot, an unbounded `events`
queue of `AgentEvent`s (exactly-once relative to the replay), and
`send_user_text`/`send` for input.
- `ThreadObserver`: synchronous `on_event(AgentEvent)` fan-out (exactly-once relative
to `on_subscribe` replay snapshots) plus awaited durability hooks
(`on_user_choice_required`/`_dismissed`, `on_commit` before tool dispatch).
- `ModelSource` (per-round model resolution; enables mid-session model switching) with
`StaticModel`; `ThreadConfigSource` (per-thread tools/prompt/notifier; enables
per-session RAP servers) with `StaticThreadConfig`; `DeferQueue` with
`InMemoryDeferQueue`/`NoDeferral`.
- `AgentEvent`/`ReplaySnapshot`/`UserChoice` display types; `Tool::is_passive()`;
in-memory `ConversationStore`/`StateStore` implementations in
`infinity_agent_core::stores`; tokio-backed sleep tools moved into the core
(`with_tokio_sleep_tools`).
- The core API is platform-neutral: `input_queue_arn` is removed from `ToolContext`
and the builder. Platform-specific configuration lives on the tools that need it
(the Lambda sleep tools now carry the input queue ARN as a field, alongside their
scheduler client, role ARN, and delay queue URL).
- All 20 daemon agent-loop tests migrated into core `system/tests.rs` with
byte-identical snapshots, plus 3 new `ThreadHandle` tests (send/receive + late
replay, survival across idle/respawn, dropped-handle pruning).
Daemon (`infinity-daemon`):
- One daemon-lifetime agent system replaces per-session agent loops. Sessions are root
threads; a `thread_exits` watcher marks sessions idle and shuts down their RAP
servers when no keep-alive client is attached.
- New `rap_servers.rs`: `ManagedRapServer`/`ManagedRapTool`/`SessionRapManager` — RAP
servers boot lazily per session via `ThreadConfigSource` and reboot transparently
after idle shutdown. Config-source info messages ("Using local config", etc.) are
preserved at first resolve.
- `DaemonObserver` implements the observer hooks (usage persistence, pending user
choices, live-attach replay including mid-stream thinking).
- `CatalogModelSource` resolves each thread's persisted model selection per round.
- Fix a pre-existing bug where the requesting client received `ModelSwitched` twice
(once via the subscriber broadcast, once via the direct reply): `switch_model` now
tracks whether the broadcast reached the requester (`same_channel`) and only sends
directly when it did not. Verified by the `switch_model_mid_session` web e2e test.
- The `chat-image-result` web golden screenshot is regenerated: the old golden
captured a layout artifact of the eager RAP-boot flow; the lazy flow renders the
canonical transcript (verified against the DOM).
- Deleted: `session/thread_worker.rs`, `session/agent_loop.rs`, `sleep_tools.rs`,
`InMemoryMessageSender`, `Session`, `spawn_session`, `AgentMessage`.
Lambda (`infinity-agent-lambda`): event handler rewritten on
`AgentSystemBuilder::new(...).build()` + `thread()` + the deferral-aware
`Thread::step` with an `EventCollector`, replacing the hand-rolled `process_batch`
plumbing.
Docs (`docs/docs/infinity-runtime`): the single "Rust API" page is replaced by two
sections. "The Agent System API" (overview, building-a-system, running-locally,
step-mode, observers) documents the new high-level API — including thread handles and
the step/step_no_defer split — and the patterns both embeddings use; "The Low-Level
API" (overview, history-manager, completion-loop) documents the platform traits and
loop pieces underneath and when to use them. Cross-references across
overview/architecture/built-in-tools/deploying-on-lambda updated.
BREAKING CHANGE: `infinity-daemon` no longer exports `sleep_tools`, `AgentMessage`,
`thread_worker`, `agent_loop`, `Session`, or `InMemoryMessageSender`; `SessionManager`'s
API is reshaped around the single agent system (`switch_model` now takes the
requester's sender and returns `Result<(), String>`) and `SharedSessionManager` is now
`Rc<tokio::Mutex<...>>`. In `infinity-agent-core`, builder tools are stored as
`Rc<dyn Tool<M>>`, `Tool` gains a defaulted `is_passive` method, `ToolContext` and
`AgentSystemBuilder` lose `input_queue_arn`, and daemon/lambda display paths now emit
`AgentEvent` instead of `DisplayEvent`.
Co-authored-by: Infinity 🤖 <infinity@hydro.run>
PR: #92
shadaj
force-pushed
the
sandbox-eb0ff46e-96e1-4d2d-8c0b-9c683fa4d1b9
branch
from
August 5, 2026 23:01
723ade3 to
794035c
Compare
shadaj
added a commit
that referenced
this pull request
Aug 10, 2026
…on + lambda onto it
Introduce `infinity_agent_core::system`, a builder-based, object-oriented API for
running agents, and rebuild both production embeddings on top of it. The docs for
`infinity-runtime` are rewritten around the new API.
Core (`infinity_agent_core::system`):
- `AgentSystemBuilder`: two construction modes — `new(stores, model, sender)` builds a
step-mode `AgentSystem` for serverless platforms; `new_local(stores, model)` builds a
`LocalAgentSystem` with an internal in-process queue (`ChannelSender`).
- Step mode's whole public surface is `AgentSystem::step(inputs, observer, defer)`:
the batch may span multiple threads (e.g. an SQS FIFO delivery with batch size > 1
interleaving several message groups). `step` partitions by `group_id`, applies the
deferral policy per thread, and joins the per-thread steps concurrently — each
loads its thread's state from the stores, prepares inputs, runs at most one
completion round, commits durably (history sync → observer commit barrier), and
dispatches at most one asynchronous tool call. Returns each thread's
`StepOutcome`; one thread's failure never aborts another mid-commit. `&mut self`
serializes calls per system instance; the internal `Thread` type (with
`filter_deferrable`/`step_no_defer` composition points used by the local driver)
is deliberately not public and `AgentSystem` is not `Clone`, so two live in-memory
views of the same thread cannot exist through the public API. Per-thread
configuration is resolved lazily on first step so loading a thread never boots
tool servers.
- `LocalAgentSystem::start` runs a full actor-system-style runtime: a router plus one
driver task per active thread, with input batching, `<interrupt>` handling for user
text during a completion, deferral of subscription events while a non-passive tool
call is pending, auto-compaction above 75% of the context window, and race-free idle
exit/respawn. `RunningSystem` exposes `sender()`, `send_user_text`, ack'd
`subscribe`, `active_threads`, a `thread_exits` channel for per-conversation resource
teardown, and `begin_shutdown` for process exit. The system itself runs for the
process lifetime.
- `ThreadHandle`: `LocalAgentSystem::start_with_handles()` runs the system with a
built-in channel-based observer (`HandleObserver`) whose subscriber registry is
shared across driver respawns; `RunningSystem::thread_handle(thread_id)` then
attaches to a thread (creating its subscription even before the thread exists) and
returns a handle with `replay()` for the initial snapshot, an unbounded `events`
queue of `AgentEvent`s (exactly-once relative to the replay), and
`send_user_text`/`send` for input. This is the only per-thread interface of a local
system.
- `ThreadObserver`: synchronous `on_event(AgentEvent)` fan-out (exactly-once relative
to `on_subscribe` replay snapshots) plus awaited durability hooks
(`on_user_choice_required`/`_dismissed`, `on_commit` before tool dispatch).
`EventCollector` buffers `(thread_id, event)` pairs.
- `ModelSource` (per-round model resolution; enables mid-session model switching) with
`StaticModel`; `ThreadConfigSource` (per-thread tools/prompt/notifier; enables
per-session RAP servers and toolsets) with `StaticThreadConfig`; `DeferQueue` with
`InMemoryDeferQueue`/`NoDeferral`.
- `AgentEvent`/`ReplaySnapshot`/`UserChoice` display types; `Tool::is_passive()`;
in-memory `ConversationStore`/`StateStore` implementations in
`infinity_agent_core::stores`; tokio-backed sleep tools moved into the core
(`with_tokio_sleep_tools`).
- The core API is platform-neutral: `input_queue_arn` is removed from `ToolContext`
and the builder. Platform-specific configuration lives on the tools that need it
(the Lambda sleep tools now carry the input queue ARN as a field).
- All 20 daemon agent-loop tests migrated into core `system/tests.rs` with
byte-identical snapshots, plus new tests: 3 `ThreadHandle` tests (send/receive +
late replay, survival across idle/respawn, dropped-handle pruning) and a
multi-group step-mode batch test.
Daemon (`infinity-daemon`):
- One daemon-lifetime agent system replaces per-session agent loops. Sessions are root
threads; a `thread_exits` watcher marks sessions idle and shuts down their RAP
servers when no keep-alive client is attached.
- New `rap_servers.rs`: `ManagedRapServer`/`ManagedRapTool`/`SessionRapManager` — RAP
servers boot lazily per session via `ThreadConfigSource` and reboot transparently
after idle shutdown. Config-source info messages ("Using local config", etc.) are
preserved at first resolve.
- `DaemonObserver` implements the observer hooks (usage persistence, pending user
choices, live-attach replay including mid-stream thinking).
- `CatalogModelSource` resolves each thread's persisted model selection per round.
- Fix a pre-existing bug where the requesting client received `ModelSwitched` twice
(once via the subscriber broadcast, once via the direct reply): `switch_model` now
tracks whether the broadcast reached the requester (`same_channel`) and only sends
directly when it did not. Verified by the `switch_model_mid_session` web e2e test.
- Dead parameters pruned: `send_input` loses its unused emit callback and takes a
`user_driven: bool` instead of an unused `Option<Subscriber>`; obsolete
`connection_keeps_alive` tracking removed from the client handler.
- The `chat-image-result` and `chat-diff-result` web golden screenshots are
regenerated: the old goldens captured a layout artifact of the eager RAP-boot flow;
the lazy flow renders the canonical transcript (verified against the DOM).
- Deleted: `session/thread_worker.rs`, `session/agent_loop.rs`, `sleep_tools.rs`,
`InMemoryMessageSender`, `Session`, `spawn_session`, `AgentMessage`.
Lambda (`infinity-agent-lambda`): event handler rewritten on the new API. One system
per invocation with a `ThreadConfigSource` (`LambdaThreadConfig`) that resolves each
thread's RAP toolsets through the DynamoDB manifest cache and adds the platform sleep
tools — correct even when a FIFO batch spans several sessions (only `batchSize: 1` is
deployed today, but the handler no longer assumes it). One `AgentSystem::step` call
processes the whole batch; per-thread outputs are aggregated in a `BTreeMap` and sent
to the output queue with each thread's root metadata.
Docs (`docs/docs/infinity-runtime`): the single "Rust API" page is replaced by two
sections. "The Agent System API" (overview, building-a-system, running-locally,
step-mode, observers) documents the new high-level API — thread handles for local
systems, one batch-shaped `step` call for serverless — and the patterns both
embeddings use; "The Low-Level API" (overview, history-manager, completion-loop)
documents the platform traits and loop pieces underneath and when to use them.
Cross-references across overview/architecture/built-in-tools/deploying-on-lambda
updated; `cargo doc` is warning-free.
BREAKING CHANGE: `infinity-daemon` no longer exports `sleep_tools`, `AgentMessage`,
`thread_worker`, `agent_loop`, `Session`, or `InMemoryMessageSender`; `SessionManager`'s
API is reshaped around the single agent system (`switch_model` takes the requester's
sender and returns `Result<(), String>`; `send_input` takes `user_driven: bool`) and
`SharedSessionManager` is now `Rc<tokio::Mutex<...>>`. In `infinity-agent-core`,
builder tools are stored as `Rc<dyn Tool<M>>`, `Tool` gains a defaulted `is_passive`
method, `ToolContext` and `AgentSystemBuilder` lose `input_queue_arn`, `Thread` is no
longer public (use `AgentSystem::step` — now batch-shaped, returning per-thread
outcomes — or `ThreadHandle`), `AgentSystem` is not `Clone`, `EventCollector::take`
returns `(thread_id, event)` pairs, and daemon/lambda display paths now emit
`AgentEvent` instead of `DisplayEvent`.
Co-authored-by: Infinity 🤖 <infinity@hydro.run>
PR: #92
shadaj
force-pushed
the
sandbox-eb0ff46e-96e1-4d2d-8c0b-9c683fa4d1b9
branch
from
August 10, 2026 21:29
794035c to
608cb0f
Compare
shadaj
added a commit
that referenced
this pull request
Aug 10, 2026
…on + lambda onto it
Introduce `infinity_agent_core::system`, a builder-based, object-oriented API for
running agents, and rebuild both production embeddings on top of it. The docs for
`infinity-runtime` are rewritten around the new API.
Core (`infinity_agent_core::system`):
- `AgentSystemBuilder`: two construction modes — `new(stores, model, sender)` builds a
step-mode `AgentSystem` for serverless platforms; `new_local(stores, model)` builds a
`LocalAgentSystem` with an internal in-process queue (`ChannelSender`).
- Step mode's whole public surface is `AgentSystem::step(inputs, observer, defer)`:
the batch may span multiple threads (e.g. an SQS FIFO delivery with batch size > 1
interleaving several message groups). `step` partitions by `group_id`, applies the
deferral policy per thread, and joins the per-thread steps concurrently — each
loads its thread's state from the stores, prepares inputs, runs at most one
completion round, commits durably (history sync → observer commit barrier), and
dispatches at most one asynchronous tool call. Returns each thread's
`StepOutcome`; one thread's failure never aborts another mid-commit. `&mut self`
serializes calls per system instance; the internal `Thread` type (with
`filter_deferrable`/`step_no_defer` composition points used by the local driver)
is deliberately not public and `AgentSystem` is not `Clone`, so two live in-memory
views of the same thread cannot exist through the public API. Per-thread
configuration is resolved lazily on first step so loading a thread never boots
tool servers.
- `LocalAgentSystem::start` runs a full actor-system-style runtime: a router plus one
driver task per active thread, with input batching, `<interrupt>` handling for user
text during a completion, deferral of subscription events while a non-passive tool
call is pending, auto-compaction above 75% of the context window, and race-free idle
exit/respawn. `RunningSystem` exposes `sender()`, `send_user_text`, ack'd
`subscribe`, `active_threads`, a `thread_exits` channel for per-conversation resource
teardown, and `begin_shutdown` for process exit. The system itself runs for the
process lifetime.
- `ThreadHandle`: `LocalAgentSystem::start_with_handles()` runs the system with a
built-in channel-based observer (`HandleObserver`) whose subscriber registry is
shared across driver respawns; `RunningSystem::thread_handle(thread_id)` then
attaches to a thread (creating its subscription even before the thread exists) and
returns a handle with `replay()` for the initial snapshot, an unbounded `events`
queue of `AgentEvent`s (exactly-once relative to the replay), and
`send_user_text`/`send` for input. This is the only per-thread interface of a local
system.
- `ThreadObserver`: synchronous `on_event(AgentEvent)` fan-out (exactly-once relative
to `on_subscribe` replay snapshots) plus awaited durability hooks
(`on_user_choice_required`/`_dismissed`, `on_commit` before tool dispatch).
`EventCollector` buffers `(thread_id, event)` pairs.
- `ModelSource` (per-round model resolution; enables mid-session model switching) with
`StaticModel`; `ThreadConfigSource` (per-thread tools/prompt/notifier; enables
per-session RAP servers and toolsets) with `StaticThreadConfig`; `DeferQueue` with
`InMemoryDeferQueue`/`NoDeferral`.
- `AgentEvent`/`ReplaySnapshot`/`UserChoice` display types; `Tool::is_passive()`;
in-memory `ConversationStore`/`StateStore` implementations in
`infinity_agent_core::stores`; tokio-backed sleep tools moved into the core
(`with_tokio_sleep_tools`).
- The core API is platform-neutral: `input_queue_arn` is removed from `ToolContext`
and the builder. Platform-specific configuration lives on the tools that need it
(the Lambda sleep tools now carry the input queue ARN as a field).
- All 20 daemon agent-loop tests migrated into core `system/tests.rs` with
byte-identical snapshots, plus new tests: 3 `ThreadHandle` tests (send/receive +
late replay, survival across idle/respawn, dropped-handle pruning) and a
multi-group step-mode batch test.
Daemon (`infinity-daemon`):
- One daemon-lifetime agent system replaces per-session agent loops. Sessions are root
threads; a `thread_exits` watcher marks sessions idle and shuts down their RAP
servers when no keep-alive client is attached.
- New `rap_servers.rs`: `ManagedRapServer`/`ManagedRapTool`/`SessionRapManager` — RAP
servers boot lazily per session via `ThreadConfigSource` and reboot transparently
after idle shutdown. Config-source info messages ("Using local config", etc.) are
preserved at first resolve.
- `DaemonObserver` implements the observer hooks (usage persistence, pending user
choices, live-attach replay including mid-stream thinking).
- `CatalogModelSource` resolves each thread's persisted model selection per round.
- Fix a pre-existing bug where the requesting client received `ModelSwitched` twice
(once via the subscriber broadcast, once via the direct reply): `switch_model` now
tracks whether the broadcast reached the requester (`same_channel`) and only sends
directly when it did not. Verified by the `switch_model_mid_session` web e2e test.
- Dead parameters pruned: `send_input` loses its unused emit callback and takes a
`user_driven: bool` instead of an unused `Option<Subscriber>`; obsolete
`connection_keeps_alive` tracking removed from the client handler.
- The `chat-image-result` and `chat-diff-result` web golden screenshots are
regenerated: the old goldens captured a layout artifact of the eager RAP-boot flow;
the lazy flow renders the canonical transcript (verified against the DOM).
- Deleted: `session/thread_worker.rs`, `session/agent_loop.rs`, `sleep_tools.rs`,
`InMemoryMessageSender`, `Session`, `spawn_session`, `AgentMessage`.
Lambda (`infinity-agent-lambda`): event handler rewritten on the new API. One system
per invocation with a `ThreadConfigSource` (`LambdaThreadConfig`) that resolves each
thread's RAP toolsets through the DynamoDB manifest cache and adds the platform sleep
tools — correct even when a FIFO batch spans several sessions (only `batchSize: 1` is
deployed today, but the handler no longer assumes it). One `AgentSystem::step` call
processes the whole batch; per-thread outputs are aggregated in a `BTreeMap` and sent
to the output queue with each thread's root metadata.
Docs (`docs/docs/infinity-runtime`): the single "Rust API" page is replaced by two
sections. "The Agent System API" (overview, building-a-system, running-locally,
step-mode, observers) documents the new high-level API — thread handles for local
systems, one batch-shaped `step` call for serverless — and the patterns both
embeddings use; "The Low-Level API" (overview, history-manager, completion-loop)
documents the platform traits and loop pieces underneath and when to use them.
Cross-references across overview/architecture/built-in-tools/deploying-on-lambda
updated; `cargo doc` is warning-free.
BREAKING CHANGE: `infinity-daemon` no longer exports `sleep_tools`, `AgentMessage`,
`thread_worker`, `agent_loop`, `Session`, or `InMemoryMessageSender`; `SessionManager`'s
API is reshaped around the single agent system (`switch_model` takes the requester's
sender and returns `Result<(), String>`; `send_input` takes `user_driven: bool`) and
`SharedSessionManager` is now `Rc<tokio::Mutex<...>>`. In `infinity-agent-core`,
builder tools are stored as `Rc<dyn Tool<M>>`, `Tool` gains a defaulted `is_passive`
method, `ToolContext` and `AgentSystemBuilder` lose `input_queue_arn`, `Thread` is no
longer public (use `AgentSystem::step` — now batch-shaped, returning per-thread
outcomes — or `ThreadHandle`), `AgentSystem` is not `Clone`, `EventCollector::take`
returns `(thread_id, event)` pairs, and daemon/lambda display paths now emit
`AgentEvent` instead of `DisplayEvent`.
Co-authored-by: Infinity 🤖 <infinity@hydro.run>
PR: #92
shadaj
force-pushed
the
sandbox-eb0ff46e-96e1-4d2d-8c0b-9c683fa4d1b9
branch
from
August 10, 2026 21:45
608cb0f to
fc4b88c
Compare
shadaj
added a commit
that referenced
this pull request
Aug 11, 2026
…on + lambda onto it
Introduce `infinity_agent_core::system`, a builder-based, object-oriented API for
running agents, and rebuild both production embeddings on top of it. The docs for
`infinity-runtime` are rewritten around the new API.
Core (`infinity_agent_core::system`):
- `AgentSystemBuilder`: two construction modes — `new(stores, model, sender)` builds a
step-mode `AgentSystem` for serverless platforms; `new_local(stores, model)` builds a
`LocalAgentSystem` with an internal in-process queue (`ChannelSender`).
- Step mode's whole public surface is `AgentSystem::step(inputs, observer, defer)`:
the batch may span multiple threads (e.g. an SQS FIFO delivery with batch size > 1
interleaving several message groups). `step` partitions by `group_id`, applies the
deferral policy per thread, and joins the per-thread steps concurrently — each
loads its thread's state from the stores, prepares inputs, runs at most one
completion round, commits durably (history sync → observer commit barrier), and
dispatches at most one asynchronous tool call. Returns each thread's
`StepOutcome`; one thread's failure never aborts another mid-commit. `&mut self`
serializes calls per system instance; the internal `Thread` type (with
`filter_deferrable`/`step_no_defer` composition points used by the local driver)
is deliberately not public and `AgentSystem` is not `Clone`, so two live in-memory
views of the same thread cannot exist through the public API. Per-thread
configuration is resolved lazily on first step so loading a thread never boots
tool servers.
- `LocalAgentSystem::start` runs a full actor-system-style runtime: a router plus one
driver task per active thread, with input batching, `<interrupt>` handling for user
text during a completion, deferral of subscription events while a non-passive tool
call is pending, auto-compaction above 75% of the context window, and race-free idle
exit/respawn. `RunningSystem` exposes `sender()`, `send_user_text`, ack'd
`subscribe`, `active_threads`, a `thread_exits` channel for per-conversation resource
teardown, and `begin_shutdown` for process exit. The system itself runs for the
process lifetime.
- `ThreadHandle`: `LocalAgentSystem::start_with_handles()` runs the system with a
built-in channel-based observer (`HandleObserver`) whose subscriber registry is
shared across driver respawns; `RunningSystem::thread_handle(thread_id)` then
attaches to a thread (creating its subscription even before the thread exists) and
returns a handle with `replay()` for the initial snapshot, an unbounded `events`
queue of `AgentEvent`s (exactly-once relative to the replay), and
`send_user_text`/`send` for input. This is the only per-thread interface of a local
system.
- `ThreadObserver`: synchronous `on_event(AgentEvent)` fan-out (exactly-once relative
to `on_subscribe` replay snapshots) plus awaited durability hooks
(`on_user_choice_required`/`_dismissed`, `on_commit` before tool dispatch).
`EventCollector` buffers `(thread_id, event)` pairs.
- `ModelSource` (per-round model resolution; enables mid-session model switching) with
`StaticModel`; `ThreadConfigSource` (per-thread tools/prompt/notifier; enables
per-session RAP servers and toolsets) with `StaticThreadConfig`; `DeferQueue` with
`InMemoryDeferQueue`/`NoDeferral`.
- `AgentEvent`/`ReplaySnapshot`/`UserChoice` display types; `Tool::is_passive()`;
in-memory `ConversationStore`/`StateStore` implementations in
`infinity_agent_core::stores`; tokio-backed sleep tools moved into the core
(`with_tokio_sleep_tools`).
- The core API is platform-neutral: `input_queue_arn` is removed from `ToolContext`
and the builder. Platform-specific configuration lives on the tools that need it
(the Lambda sleep tools now carry the input queue ARN as a field).
- All 20 daemon agent-loop tests migrated into core `system/tests.rs` with
byte-identical snapshots, plus new tests: 3 `ThreadHandle` tests (send/receive +
late replay, survival across idle/respawn, dropped-handle pruning) and a
multi-group step-mode batch test.
Daemon (`infinity-daemon`):
- One daemon-lifetime agent system replaces per-session agent loops. Sessions are root
threads; a `thread_exits` watcher marks sessions idle and shuts down their RAP
servers when no keep-alive client is attached.
- New `rap_servers.rs`: `ManagedRapServer`/`ManagedRapTool`/`SessionRapManager` — RAP
servers boot lazily per session via `ThreadConfigSource` and reboot transparently
after idle shutdown. Config-source info messages ("Using local config", etc.) are
preserved at first resolve.
- `DaemonObserver` implements the observer hooks (usage persistence, pending user
choices, live-attach replay including mid-stream thinking).
- `CatalogModelSource` resolves each thread's persisted model selection per round.
- Fix a pre-existing bug where the requesting client received `ModelSwitched` twice
(once via the subscriber broadcast, once via the direct reply): `switch_model` now
tracks whether the broadcast reached the requester (`same_channel`) and only sends
directly when it did not. Verified by the `switch_model_mid_session` web e2e test.
- Dead parameters pruned: `send_input` loses its unused emit callback and takes a
`user_driven: bool` instead of an unused `Option<Subscriber>`; obsolete
`connection_keeps_alive` tracking removed from the client handler.
- The `chat-image-result` and `chat-diff-result` web golden screenshots are
regenerated: the old goldens captured a layout artifact of the eager RAP-boot flow;
the lazy flow renders the canonical transcript (verified against the DOM).
- Deleted: `session/thread_worker.rs`, `session/agent_loop.rs`, `sleep_tools.rs`,
`InMemoryMessageSender`, `Session`, `spawn_session`, `AgentMessage`.
Lambda (`infinity-agent-lambda`): event handler rewritten on the new API. One system
per invocation with a `ThreadConfigSource` (`LambdaThreadConfig`) that resolves each
thread's RAP toolsets through the DynamoDB manifest cache and adds the platform sleep
tools — correct even when a FIFO batch spans several sessions (only `batchSize: 1` is
deployed today, but the handler no longer assumes it). One `AgentSystem::step` call
processes the whole batch; per-thread outputs are aggregated in a `BTreeMap` and sent
to the output queue with each thread's root metadata.
Docs (`docs/docs/infinity-runtime`): the single "Rust API" page is replaced by two
sections. "The Agent System API" (overview, building-a-system, running-locally,
step-mode, observers) documents the new high-level API — thread handles for local
systems, one batch-shaped `step` call for serverless — and the patterns both
embeddings use; "The Low-Level API" (overview, history-manager, completion-loop)
documents the platform traits and loop pieces underneath and when to use them.
Cross-references across overview/architecture/built-in-tools/deploying-on-lambda
updated; `cargo doc` is warning-free.
BREAKING CHANGE: `infinity-daemon` no longer exports `sleep_tools`, `AgentMessage`,
`thread_worker`, `agent_loop`, `Session`, or `InMemoryMessageSender`; `SessionManager`'s
API is reshaped around the single agent system (`switch_model` takes the requester's
sender and returns `Result<(), String>`; `send_input` takes `user_driven: bool`) and
`SharedSessionManager` is now `Rc<tokio::Mutex<...>>`. In `infinity-agent-core`,
builder tools are stored as `Rc<dyn Tool<M>>`, `Tool` gains a defaulted `is_passive`
method, `ToolContext` and `AgentSystemBuilder` lose `input_queue_arn`, `Thread` is no
longer public (use `AgentSystem::step` — now batch-shaped, returning per-thread
outcomes — or `ThreadHandle`), `AgentSystem` is not `Clone`, `EventCollector::take`
returns `(thread_id, event)` pairs, and daemon/lambda display paths now emit
`AgentEvent` instead of `DisplayEvent`.
Co-authored-by: Infinity 🤖 <infinity@hydro.run>
PR: #92
shadaj
force-pushed
the
sandbox-eb0ff46e-96e1-4d2d-8c0b-9c683fa4d1b9
branch
from
August 11, 2026 00:12
fc4b88c to
6840c02
Compare
shadaj
added a commit
that referenced
this pull request
Aug 11, 2026
…on + lambda onto it Introduce `infinity_agent_core::system`, a builder-based, object-oriented API for running agents, rebuild both production embeddings on top of it, unify the parallel implementations that had grown around the old architecture, and rewrite the `infinity-runtime` docs around the new API. Core (`infinity_agent_core::system`): - `AgentSystemBuilder`: `new(stores, model, sender)` builds a step-mode `AgentSystem` for serverless platforms; `new_local(stores, model)` builds a `LocalAgentSystem` with an internal in-process queue (`ChannelSender`). - Step mode's public surface is `AgentSystem::step(inputs, observer, defer)`: the batch may span multiple threads (SQS FIFO batches can interleave message groups); `step` partitions by `group_id`, applies the deferral policy per thread, joins the per-thread slices concurrently (prepare → completion → history sync → observer commit barrier → tool dispatch), and returns each thread's `StepOutcome`. `&mut self` serializes calls per system; the internal `Thread` type is not public and `AgentSystem` is not `Clone`, so two live in-memory views of one thread cannot exist through the public API. Per-thread configuration resolves lazily on first step so loading a thread never boots tool servers. - `LocalAgentSystem::start` runs an actor-system-style runtime: a router plus one driver per active thread, with input batching, `<interrupt>` handling, deferral of synthetic events while a non-passive tool call is pending, auto-compaction above 75% of the context window, and race-free idle exit/respawn. `RunningSystem` exposes senders, ack'd `subscribe`, `active_threads`, a `thread_exits` channel for per-conversation resource teardown, and `begin_shutdown` for process exit. - `ThreadHandle` (`start_with_handles()` + `thread_handle(id)`): attach to a thread (even before it exists), get `replay()` plus an exactly-once `events` stream, and send input; subscriptions survive driver respawns via a shared registry. - `ThreadObserver`: synchronous `on_event(AgentEvent)` fan-out (exactly-once relative to `on_subscribe` replays) plus awaited durability hooks (`on_user_choice_required` /`_dismissed`, `on_commit` before tool dispatch). `EventCollector` buffers `(thread_id, event)` pairs. - `ModelSource` (per-round resolution; enables mid-session model switching), `ThreadConfigSource` (per-thread tools/prompt/notifier; enables per-session RAP servers and toolsets), `DeferQueue` (`InMemoryDeferQueue`/`NoDeferral`), `Tool::is_passive()`, tokio sleep tools (`with_tokio_sleep_tools`). - **`batch_processor` deleted**: `process_batch` had no production callers and its semantics violated the documented durability barrier (swallowed sync errors, then dispatched; emitted ResponseDone before sync). The documented low-level API is now `HistoryManager` + `prepare_input`/`run_completion`/`execute_action`; `execute_action_with_error_result` (the #88 error-fallback) lives in `event_processor`. `DisplayEvent` moved out of core: `AgentEvent` is core's only event vocabulary; the CLI keeps a local, non-generic `DisplayEvent` as its terminal-rendering enum. All unique regression coverage ported. - **Shared slice internals**: `event_processor::input_echo` is the single computation of accepted-input echoes (was duplicated between the system path and the old batch path); `SyntheticKind::is_compaction_complete()`; `InputMessage::user_text()`; driver's in-flight step modeled as `InFlightStep { fut, cancel_tx }` with a consuming `cancel()`. - **`rap_callback` module**: the RAP callback → `InputMessage` conversion (multimodal content, display segments, tagged synthetics incl. subscription final/associative flags, user_choice, oauth) now lives in core with 11 unit tests; the daemon delegates to it. - In-memory `ConversationStore`/`StateStore` in `infinity_agent_core::stores` with a serializable snapshot/restore API; the daemon's stores are now thin wrappers (extras + JSON persistence) so the subtle ancestor/compaction/dedup semantics exist in exactly one place. `spawn_thread_with_id` supports deterministic IDs. - Platform-neutral core: `input_queue_arn` removed from `ToolContext`/builder (Lambda sleep tools carry it as a field). - All 20 daemon agent-loop tests migrated into core `system/tests.rs` with byte-identical snapshots, plus new coverage: `ThreadHandle` streaming across tool-call rounds (CompletionFinished is per round; consumers keep pulling while a call is pending), multi-group step batches, failed-dispatch fallback, OAuth surfacing, prepare-level user-choice, handle respawn survival and pruning. Daemon (`infinity-daemon`): - One daemon-lifetime agent system replaces per-session agent loops; sessions are root threads; a `thread_exits` watcher (via the single `session_has_active_threads` predicate) marks sessions idle and shuts down their RAP servers when no keep-alive client is attached. - `rap_servers.rs`: `ManagedRapServer` boots lazily per session via `ThreadConfigSource` and reboots transparently after idle shutdown; `Up` state caches the manifest (fetch failure tears the boot down — no half-up state); MCP proxy tasks are owned (stdio children `kill_on_drop`) but proxies stay up across idles since MCP servers may be stateful; migration flows ride the same `collect_server_specs` + `ensure_up` path (`MigrationServer`; `boot_rap_servers`/`BootedRapServers`/`rap_tools.rs` deleted); RAP config reading/merging unified in `config::load_merged_rap_config`. - `DaemonObserver` implements the observer hooks; subscriber broadcasting unified in `observer::broadcast_pruning`/`broadcast_to_thread` (fixing `handle_view_update` never pruning dead subscribers); re-sent Connects replace rather than stack subscriptions; replay rendering now matches live rendering (strips `<interrupt>` prefixes, uses `rap_protocol::build_display_segments`). - `CatalogModelSource` resolves each thread's persisted model per round. Fix the pre-existing double `ModelSwitched` delivery (`switch_model` tracks whether the broadcast reached the requester via `same_channel`). - `send_input` takes `user_driven: bool`; `SessionManager` methods that only touch interior-mutable state take `&self`. - Web goldens `chat-image-result`/`chat-diff-result` regenerated (old goldens captured an eager-boot layout artifact). - Deleted: `session/thread_worker.rs`, `session/agent_loop.rs`, `sleep_tools.rs`, `InMemoryMessageSender`, `Session`, `spawn_session`, `AgentMessage`. Lambda (`infinity-agent-lambda`): - Event handler rewritten on the new API: one system per invocation with a `ThreadConfigSource` resolving each thread's RAP toolsets (DynamoDB manifest cache) plus platform sleep tools; one batch-shaped `step`; per-thread outputs aggregated in a `BTreeMap`. Sleep tools share a `WakeupScheduler` (SQS delay / EventBridge dispatch deduplicated). - **New `rap-receiver` binary replaces `agent/lib/infinity-agents/rap-receiver/ index.mjs`** (deleted): same Function-URL contract, but full parity with the daemon's conversion via `infinity_agent_core::rap_callback` — user_choice callbacks accepted (JS returned 400), multimodal content and `display_as` preserved, subscription `final` flag and tagged synthetics honored, and stable dedup IDs for results/oauth/choices (per-delivery IDs only for subscription events, which share a tool_call_id). CDK switches the receiver to a cargo-lambda `RustFunction`. Deploy note: the Function URL is recreated, so persisted callback URLs from long-lived subscriptions must be re-subscribed. Docs (`docs/docs/infinity-runtime`): the single "Rust API" page is replaced by two sections rewritten in a usage-oriented style (example-first walkthroughs rather than API inventories): "The Agent System API" (overview, building-a-system, running-locally, step-mode, observers) and "The Low-Level API" (overview, history-manager, completion-loop). Cross-references updated repo-wide; `cargo doc` is warning-free. BREAKING CHANGE: `infinity-agent-core` removes `batch_processor` (`process_batch`, `process_input_item`, `DisplayEvent`); `Thread` is no longer public (use `AgentSystem::step` or `ThreadHandle`); `AgentSystem` is not `Clone`; `EventCollector::take` returns `(thread_id, event)` pairs; builder tools are stored as `Rc<dyn Tool<M>>`; `Tool` gains defaulted `is_passive`; `ToolContext`/builder lose `input_queue_arn`. `infinity-daemon` no longer exports `sleep_tools`, `AgentMessage`, `thread_worker`, `agent_loop`, `Session`, `InMemoryMessageSender`, `boot_rap_servers`, or `rap_tools`; `SessionManager::switch_model` takes the requester's sender and returns `Result<(), String>`; `send_input` takes `user_driven: bool`; `SharedSessionManager` is `Rc<tokio::Mutex<...>>`. The CLI's `DisplayEvent` now lives in `infinity_agent_cli::display`. Co-authored-by: Infinity 🤖 <infinity@hydro.run> PR: #92
shadaj
force-pushed
the
sandbox-eb0ff46e-96e1-4d2d-8c0b-9c683fa4d1b9
branch
from
August 11, 2026 23:08
6840c02 to
ccb90b9
Compare
shadaj
added a commit
that referenced
this pull request
Aug 11, 2026
…on + lambda onto it Introduce `infinity_agent_core::system`, a builder-based, object-oriented API for running agents, rebuild both production embeddings on top of it, unify the parallel implementations that had grown around the old architecture, and rewrite the `infinity-runtime` docs around the new API. Core (`infinity_agent_core::system`): - `AgentSystemBuilder`: `new(stores, model, sender)` builds a step-mode `AgentSystem` for serverless platforms; `new_local(stores, model)` builds a `LocalAgentSystem` with an internal in-process queue (`ChannelSender`). - Step mode's public surface is `AgentSystem::step(inputs, observer, defer)`: the batch may span multiple threads (SQS FIFO batches can interleave message groups); `step` partitions by `group_id`, applies the deferral policy per thread, joins the per-thread slices concurrently (prepare → completion → history sync → observer commit barrier → tool dispatch), and returns each thread's `StepOutcome`. `&mut self` serializes calls per system; the internal `Thread` type is not public and `AgentSystem` is not `Clone`, so two live in-memory views of one thread cannot exist through the public API. Per-thread configuration resolves lazily on first step so loading a thread never boots tool servers. - `LocalAgentSystem::start` runs an actor-system-style runtime: a router plus one driver per active thread, with input batching, `<interrupt>` handling, deferral of synthetic events while a non-passive tool call is pending, auto-compaction above 75% of the context window, and race-free idle exit/respawn. `RunningSystem` exposes senders, ack'd `subscribe`, `active_threads`, a `thread_exits` channel for per-conversation resource teardown, and `begin_shutdown` for process exit. - `ThreadHandle` (`start_with_handles()` + `thread_handle(id)`): attach to a thread (even before it exists), get `replay()` plus an exactly-once `events` stream, and send input; subscriptions survive driver respawns via a shared registry. - `ThreadObserver`: synchronous `on_event(AgentEvent)` fan-out (exactly-once relative to `on_subscribe` replays) plus awaited durability hooks (`on_user_choice_required` /`_dismissed`, `on_commit` before tool dispatch). `EventCollector` buffers `(thread_id, event)` pairs. - `ModelSource` (per-round resolution; enables mid-session model switching), `ThreadConfigSource` (per-thread tools/prompt/notifier; enables per-session RAP servers and toolsets), `DeferQueue` (`InMemoryDeferQueue`/`NoDeferral`), `Tool::is_passive()`, tokio sleep tools (`with_tokio_sleep_tools`). - **`batch_processor` deleted**: `process_batch` had no production callers and its semantics violated the documented durability barrier (swallowed sync errors, then dispatched; emitted ResponseDone before sync). The documented low-level API is now `HistoryManager` + `prepare_input`/`run_completion`/`execute_action`; `execute_action_with_error_result` (the #88 error-fallback) lives in `event_processor`. `DisplayEvent` moved out of core: `AgentEvent` is core's only event vocabulary; the CLI keeps a local, non-generic `DisplayEvent` as its terminal-rendering enum. All unique regression coverage ported. - **Shared slice internals**: `event_processor::input_echo` is the single computation of accepted-input echoes (was duplicated between the system path and the old batch path); `SyntheticKind::is_compaction_complete()`; `InputMessage::user_text()`; driver's in-flight step modeled as `InFlightStep { fut, cancel_tx }` with a consuming `cancel()`. - **`rap_callback` module**: the RAP callback → `InputMessage` conversion (multimodal content, display segments, tagged synthetics incl. subscription final/associative flags, user_choice, oauth) now lives in core with 11 unit tests; the daemon delegates to it. - In-memory `ConversationStore`/`StateStore` in `infinity_agent_core::stores` with a serializable snapshot/restore API; the daemon's stores are now thin wrappers (extras + JSON persistence) so the subtle ancestor/compaction/dedup semantics exist in exactly one place. `spawn_thread_with_id` supports deterministic IDs. - Platform-neutral core: `input_queue_arn` removed from `ToolContext`/builder (Lambda sleep tools carry it as a field). - All 20 daemon agent-loop tests migrated into core `system/tests.rs` with byte-identical snapshots, plus new coverage: `ThreadHandle` streaming across tool-call rounds (CompletionFinished is per round; consumers keep pulling while a call is pending), multi-group step batches, failed-dispatch fallback, OAuth surfacing, prepare-level user-choice, handle respawn survival and pruning. Daemon (`infinity-daemon`): - One daemon-lifetime agent system replaces per-session agent loops; sessions are root threads; a `thread_exits` watcher (via the single `session_has_active_threads` predicate) marks sessions idle and shuts down their RAP servers when no keep-alive client is attached. - `rap_servers.rs`: `ManagedRapServer` boots lazily per session via `ThreadConfigSource` and reboots transparently after idle shutdown; `Up` state caches the manifest (fetch failure tears the boot down — no half-up state); MCP proxy tasks are owned (stdio children `kill_on_drop`) but proxies stay up across idles since MCP servers may be stateful; migration flows ride the same `collect_server_specs` + `ensure_up` path (`MigrationServer`; `boot_rap_servers`/`BootedRapServers`/`rap_tools.rs` deleted); RAP config reading/merging unified in `config::load_merged_rap_config`. - `DaemonObserver` implements the observer hooks; subscriber broadcasting unified in `observer::broadcast_pruning`/`broadcast_to_thread` (fixing `handle_view_update` never pruning dead subscribers); re-sent Connects replace rather than stack subscriptions; replay rendering now matches live rendering (strips `<interrupt>` prefixes, uses `rap_protocol::build_display_segments`). - `CatalogModelSource` resolves each thread's persisted model per round. Fix the pre-existing double `ModelSwitched` delivery (`switch_model` tracks whether the broadcast reached the requester via `same_channel`). - `send_input` takes `user_driven: bool`; `SessionManager` methods that only touch interior-mutable state take `&self`. - Web goldens `chat-image-result`/`chat-diff-result` regenerated (old goldens captured an eager-boot layout artifact). - Deleted: `session/thread_worker.rs`, `session/agent_loop.rs`, `sleep_tools.rs`, `InMemoryMessageSender`, `Session`, `spawn_session`, `AgentMessage`. Lambda (`infinity-agent-lambda`): - Event handler rewritten on the new API: one system per invocation with a `ThreadConfigSource` resolving each thread's RAP toolsets (DynamoDB manifest cache) plus platform sleep tools; one batch-shaped `step`; per-thread outputs aggregated in a `BTreeMap`. Sleep tools share a `WakeupScheduler` (SQS delay / EventBridge dispatch deduplicated). - **New `rap-receiver` binary replaces `agent/lib/infinity-agents/rap-receiver/ index.mjs`** (deleted): same Function-URL contract, but full parity with the daemon's conversion via `infinity_agent_core::rap_callback` — user_choice callbacks accepted (JS returned 400), multimodal content and `display_as` preserved, subscription `final` flag and tagged synthetics honored, and stable dedup IDs for results/oauth/choices (per-delivery IDs only for subscription events, which share a tool_call_id). CDK switches the receiver to a cargo-lambda `RustFunction`. Deploy note: the Function URL is recreated, so persisted callback URLs from long-lived subscriptions must be re-subscribed. Docs (`docs/docs/infinity-runtime`): the single "Rust API" page is replaced by two sections rewritten in a usage-oriented style (example-first walkthroughs rather than API inventories): "The Agent System API" (overview, building-a-system, running-locally, step-mode, observers) and "The Low-Level API" (overview, history-manager, completion-loop). Cross-references updated repo-wide; `cargo doc` is warning-free. BREAKING CHANGE: `infinity-agent-core` removes `batch_processor` (`process_batch`, `process_input_item`, `DisplayEvent`); `Thread` is no longer public (use `AgentSystem::step` or `ThreadHandle`); `AgentSystem` is not `Clone`; `EventCollector::take` returns `(thread_id, event)` pairs; builder tools are stored as `Rc<dyn Tool<M>>`; `Tool` gains defaulted `is_passive`; `ToolContext`/builder lose `input_queue_arn`. `infinity-daemon` no longer exports `sleep_tools`, `AgentMessage`, `thread_worker`, `agent_loop`, `Session`, `InMemoryMessageSender`, `boot_rap_servers`, or `rap_tools`; `SessionManager::switch_model` takes the requester's sender and returns `Result<(), String>`; `send_input` takes `user_driven: bool`; `SharedSessionManager` is `Rc<tokio::Mutex<...>>`. The CLI's `DisplayEvent` now lives in `infinity_agent_cli::display`. Co-authored-by: Infinity 🤖 <infinity@hydro.run> PR: #92
shadaj
force-pushed
the
sandbox-eb0ff46e-96e1-4d2d-8c0b-9c683fa4d1b9
branch
from
August 11, 2026 23:33
ccb90b9 to
ec1b0d4
Compare
shadaj
added a commit
that referenced
this pull request
Aug 12, 2026
…on + lambda onto it
Introduce `infinity_agent_core::system`, a builder-based, object-oriented API for
running agents, rebuild both production embeddings on top of it, unify the parallel
implementations that had grown around the old architecture, and rewrite the
`infinity-runtime` docs around the new API.
Core (`infinity_agent_core::system`):
- `AgentSystemBuilder`: `new(stores, model, sender)` builds a step-mode `AgentSystem`
for serverless platforms; `new_local(stores, model)` builds a `LocalAgentSystem`
with an internal in-process queue (`ChannelSender`).
- Step mode's public surface is `AgentSystem::step(inputs, observer, defer)`: the
batch may span multiple threads (SQS FIFO batches can interleave message groups);
`step` partitions by `group_id`, applies the deferral policy per thread, joins the
per-thread slices concurrently (prepare → completion → history sync → observer
commit barrier → tool dispatch), and returns each thread's `StepOutcome`. `&mut
self` serializes calls per system; the internal `Thread` type is not public and
`AgentSystem` is not `Clone`, so two live in-memory views of one thread cannot
exist through the public API. Per-thread configuration resolves lazily on first
step so loading a thread never boots tool servers.
- `LocalAgentSystem` is mode-typed (`Handles` by default, `Launcher` after
`with_thread_launcher()`), with one `start()` per mode:
- Handles mode: `start()` returns a `RunningSystem` running an actor-system-style
runtime (router + one driver per active thread, input batching, `<interrupt>`
handling, deferral while a non-passive tool call is pending, auto-compaction
above 75% of the context window, race-free idle exit/respawn) with ack'd
`subscribe`, a `thread_exits` channel, and `thread_handle(id)` attaching to any
thread. `start_with_observer(factory)` swaps in a custom `ThreadObserver`.
- Launcher mode: `start()` returns a `LaunchingSystem`. `thread_builder()`
configures and launches new threads (generated IDs) with their own tools and
prompt, unioned onto the system-wide configuration (static or
`ThreadConfigSource`) via an internal `UnionConfigSource`; `thread_handle(id)`
re-attaches to existing threads only (launched in-process or with history).
Launch configurations are process-lifetime (not persisted).
- `ThreadHandle`: `replay()` plus an exactly-once `events` stream and input sending;
subscriptions survive driver respawns via a shared registry.
- `ThreadObserver`: synchronous `on_event(AgentEvent)` fan-out (exactly-once relative
to `on_subscribe` replays) plus awaited durability hooks (`on_user_choice_required`
/`_dismissed`, `on_commit` before tool dispatch). `EventCollector` buffers
`(thread_id, event)` pairs.
- `ModelSource` (per-round resolution; enables mid-session model switching),
`ThreadConfigSource` (per-thread tools/prompt/notifier; enables per-session RAP
servers and toolsets), `DeferQueue` (`InMemoryDeferQueue`/`NoDeferral`),
`Tool::is_passive()`, tokio sleep tools (`with_tokio_sleep_tools`).
- `batch_processor` deleted: `process_batch` had no production callers and its
semantics violated the documented durability barrier (swallowed sync errors, then
dispatched; emitted ResponseDone before sync). The documented low-level API is now
`HistoryManager` + `prepare_input`/`run_completion`/`execute_action`;
`execute_action_with_error_result` (the #88 error-fallback) lives in
`event_processor`. `DisplayEvent` moved out of core: `AgentEvent` is core's only
event vocabulary; the CLI keeps a local, non-generic `DisplayEvent` as its
terminal-rendering enum. All unique regression coverage ported.
- Shared slice internals: `event_processor::input_echo` is the single computation of
accepted-input echoes; `SyntheticKind::is_compaction_complete()`;
`InputMessage::user_text()`; driver's in-flight step modeled as
`InFlightStep { fut, cancel_tx }` with a consuming `cancel()`.
- `rap_callback` module: the RAP callback → `InputMessage` conversion (multimodal
content, display segments, tagged synthetics incl. subscription final/associative
flags, user_choice, oauth) lives in core with 11 unit tests; the daemon delegates.
- In-memory `ConversationStore`/`StateStore` in `infinity_agent_core::stores` with a
serializable snapshot/restore API; the daemon's stores are thin wrappers (extras +
JSON persistence) so the ancestor/compaction/dedup semantics exist in exactly one
place. `spawn_thread_with_id` supports deterministic IDs.
- Platform-neutral core: `input_queue_arn` removed from `ToolContext`/builder.
- All 20 daemon agent-loop tests migrated into core `system/tests.rs` with
byte-identical snapshots, plus new coverage: launcher tool/prompt unioning and
existing-only attachment, `ThreadHandle` streaming across tool-call rounds,
multi-group step batches, failed-dispatch fallback, OAuth surfacing, prepare-level
user-choice, handle respawn survival and pruning.
Daemon (`infinity-daemon`):
- One daemon-lifetime agent system replaces per-session agent loops; sessions are
root threads; a `thread_exits` watcher marks sessions idle and shuts down their
RAP servers when no keep-alive client is attached.
- `rap_servers.rs`: `ManagedRapServer` boots lazily per session via
`ThreadConfigSource` and reboots transparently after idle shutdown; `Up` caches
the manifest (fetch failure tears the boot down); MCP proxy tasks are owned
(stdio children `kill_on_drop`) but proxies stay up across idles since MCP servers
may be stateful; migration flows ride the same path (`MigrationServer`;
`boot_rap_servers`/`rap_tools.rs` deleted); config merging unified in
`config::load_merged_rap_config`.
- `DaemonObserver` implements the observer hooks; broadcasting unified in
`observer::broadcast_pruning`/`broadcast_to_thread` (fixing `handle_view_update`
never pruning dead subscribers); re-sent Connects replace rather than stack
subscriptions; replay rendering matches live rendering.
- `CatalogModelSource` resolves each thread's persisted model per round; fixed the
pre-existing double `ModelSwitched` delivery.
- `send_input` takes `user_driven: bool`; interior-mutability methods take `&self`.
- Web goldens `chat-image-result`/`chat-diff-result` regenerated (old goldens
captured an eager-boot layout artifact).
- Deleted: `session/thread_worker.rs`, `session/agent_loop.rs`, `sleep_tools.rs`,
`InMemoryMessageSender`, `Session`, `spawn_session`, `AgentMessage`.
Lambda (`infinity-agent-lambda`):
- Event handler rewritten on the new API: one system per invocation with a
`ThreadConfigSource` resolving each thread's RAP toolsets (DynamoDB manifest
cache) plus platform sleep tools (shared `WakeupScheduler` backend); one
batch-shaped `step`; per-thread outputs aggregated in a `BTreeMap`.
- New `rap-receiver` binary replaces the drifted JS receiver (deleted): same
Function-URL contract, full parity with the daemon's conversion via
`infinity_agent_core::rap_callback` (user_choice accepted, multimodal and
`display_as` preserved, tagged synthetics honored, stable dedup IDs where the
wire has identity). CDK switches to a cargo-lambda `RustFunction`; the Function
URL is recreated on deploy, so persisted callback URLs must be re-subscribed.
Docs (`docs/docs/infinity-runtime`): the single "Rust API" page is replaced by two
sections, written in the Hydro docs register (definitional openings, bolded key
terms, cause-and-effect guarantees, code-first examples, admonitions for caveats):
"The Agent System API" (overview, building-a-system, running-locally, step-mode,
observers, including launcher mode) and "The Low-Level API" (overview,
history-manager, completion-loop). Cross-references updated repo-wide; `cargo doc`
is warning-free.
BREAKING CHANGE: `infinity-agent-core` removes `batch_processor` (`process_batch`,
`process_input_item`, `DisplayEvent`); `Thread` is no longer public (use
`AgentSystem::step` or `ThreadHandle`); `AgentSystem` is not `Clone`;
`LocalAgentSystem::start_with_handles` is now `start()` and custom observers use
`start_with_observer` (`LocalAgentSystem` gained a mode type parameter, default
`Handles`); `EventCollector::take` returns `(thread_id, event)` pairs; builder tools
are stored as `Rc<dyn Tool<M>>`; `Tool` gains defaulted `is_passive`;
`ToolContext`/builder lose `input_queue_arn`. `infinity-daemon` no longer exports
`sleep_tools`, `AgentMessage`, `thread_worker`, `agent_loop`, `Session`,
`InMemoryMessageSender`, `boot_rap_servers`, or `rap_tools`;
`SessionManager::switch_model` takes the requester's sender and returns
`Result<(), String>`; `send_input` takes `user_driven: bool`;
`SharedSessionManager` is `Rc<tokio::Mutex<...>>`. The CLI's `DisplayEvent` lives in
`infinity_agent_cli::display`.
Co-authored-by: Infinity 🤖 <infinity@hydro.run>
PR: #92
shadaj
force-pushed
the
sandbox-eb0ff46e-96e1-4d2d-8c0b-9c683fa4d1b9
branch
from
August 12, 2026 00:01
ec1b0d4 to
399a398
Compare
shadaj
added a commit
that referenced
this pull request
Aug 12, 2026
…on + lambda onto it
Introduce `infinity_agent_core::system`, a builder-based, object-oriented API for
running agents, rebuild both production embeddings on top of it, unify the parallel
implementations that had grown around the old architecture, and rewrite the
`infinity-runtime` docs around the new API.
Core (`infinity_agent_core::system`):
- `AgentSystemBuilder`: `new(stores, model, sender)` builds a step-mode `AgentSystem`
for serverless platforms; `new_local(stores, model)` builds a `LocalAgentSystem`
with an internal in-process queue (`ChannelSender`).
- Step mode's public surface is `AgentSystem::step(inputs, observer, defer)`: the
batch may span multiple threads (SQS FIFO batches can interleave message groups);
`step` partitions by `group_id`, applies the deferral policy per thread, joins the
per-thread slices concurrently (prepare → completion → history sync → observer
commit barrier → tool dispatch), and returns each thread's `StepOutcome`. `&mut
self` serializes calls per system; the internal `Thread` type is not public and
`AgentSystem` is not `Clone`, so two live in-memory views of one thread cannot
exist through the public API. Per-thread configuration resolves lazily on first
step so loading a thread never boots tool servers.
- `LocalAgentSystem` is mode-typed (`Handles` by default, `Launcher` after
`with_thread_launcher()`), with one `start()` per mode:
- Handles mode: `start()` returns a `RunningSystem` running an actor-system-style
runtime (router + one driver per active thread, input batching, `<interrupt>`
handling, deferral while a non-passive tool call is pending, auto-compaction
above 75% of the context window, race-free idle exit/respawn) with ack'd
`subscribe`, a `thread_exits` channel, and `thread_handle(id)` attaching to any
thread. `start_with_observer(factory)` swaps in a custom `ThreadObserver`.
- Launcher mode: `start()` returns a `LaunchingSystem`. `thread_builder()`
configures and launches new threads (generated IDs) with their own tools and
prompt, unioned onto the system-wide configuration (static or
`ThreadConfigSource`) via an internal `UnionConfigSource`; `thread_handle(id)`
re-attaches to existing threads only (launched in-process or with history).
Launch configurations are process-lifetime (not persisted).
- `ThreadHandle`: `replay()` plus an exactly-once `events` stream and input sending;
subscriptions survive driver respawns via a shared registry.
- `ThreadObserver`: synchronous `on_event(AgentEvent)` fan-out (exactly-once relative
to `on_subscribe` replays) plus awaited durability hooks (`on_user_choice_required`
/`_dismissed`, `on_commit` before tool dispatch). `EventCollector` buffers
`(thread_id, event)` pairs.
- `ModelSource` (per-round resolution; enables mid-session model switching),
`ThreadConfigSource` (per-thread tools/prompt/notifier; enables per-session RAP
servers and toolsets), `DeferQueue` (`InMemoryDeferQueue`/`NoDeferral`),
`Tool::is_passive()`, tokio sleep tools (`with_tokio_sleep_tools`).
- `batch_processor` deleted: `process_batch` had no production callers and its
semantics violated the documented durability barrier (swallowed sync errors, then
dispatched; emitted ResponseDone before sync). The documented low-level API is now
`HistoryManager` + `prepare_input`/`run_completion`/`execute_action`;
`execute_action_with_error_result` (the #88 error-fallback) lives in
`event_processor`. `DisplayEvent` moved out of core: `AgentEvent` is core's only
event vocabulary; the CLI keeps a local, non-generic `DisplayEvent` as its
terminal-rendering enum. All unique regression coverage ported.
- Shared slice internals: `event_processor::input_echo` is the single computation of
accepted-input echoes; `SyntheticKind::is_compaction_complete()`;
`InputMessage::user_text()`; driver's in-flight step modeled as
`InFlightStep { fut, cancel_tx }` with a consuming `cancel()`.
- `rap_callback` module: the RAP callback → `InputMessage` conversion (multimodal
content, display segments, tagged synthetics incl. subscription final/associative
flags, user_choice, oauth) lives in core with 11 unit tests; the daemon delegates.
- In-memory `ConversationStore`/`StateStore` in `infinity_agent_core::stores` with a
serializable snapshot/restore API; the daemon's stores are thin wrappers (extras +
JSON persistence) so the ancestor/compaction/dedup semantics exist in exactly one
place. `spawn_thread_with_id` supports deterministic IDs.
- Platform-neutral core: `input_queue_arn` removed from `ToolContext`/builder.
- All 20 daemon agent-loop tests migrated into core `system/tests.rs` with
byte-identical snapshots, plus new coverage: launcher tool/prompt unioning and
existing-only attachment, `ThreadHandle` streaming across tool-call rounds,
multi-group step batches, failed-dispatch fallback, OAuth surfacing, prepare-level
user-choice, handle respawn survival and pruning.
Daemon (`infinity-daemon`):
- One daemon-lifetime agent system replaces per-session agent loops; sessions are
root threads; a `thread_exits` watcher marks sessions idle and shuts down their
RAP servers when no keep-alive client is attached.
- `rap_servers.rs`: `ManagedRapServer` boots lazily per session via
`ThreadConfigSource` and reboots transparently after idle shutdown; `Up` caches
the manifest (fetch failure tears the boot down); MCP proxy tasks are owned
(stdio children `kill_on_drop`) but proxies stay up across idles since MCP servers
may be stateful; migration flows ride the same path (`MigrationServer`;
`boot_rap_servers`/`rap_tools.rs` deleted); config merging unified in
`config::load_merged_rap_config`.
- `DaemonObserver` implements the observer hooks; broadcasting unified in
`observer::broadcast_pruning`/`broadcast_to_thread` (fixing `handle_view_update`
never pruning dead subscribers); re-sent Connects replace rather than stack
subscriptions; replay rendering matches live rendering.
- `CatalogModelSource` resolves each thread's persisted model per round; fixed the
pre-existing double `ModelSwitched` delivery.
- `send_input` takes `user_driven: bool`; interior-mutability methods take `&self`.
- Web goldens `chat-image-result`/`chat-diff-result` regenerated (old goldens
captured an eager-boot layout artifact).
- Deleted: `session/thread_worker.rs`, `session/agent_loop.rs`, `sleep_tools.rs`,
`InMemoryMessageSender`, `Session`, `spawn_session`, `AgentMessage`.
Lambda (`infinity-agent-lambda`):
- Event handler rewritten on the new API: one system per invocation with a
`ThreadConfigSource` resolving each thread's RAP toolsets (DynamoDB manifest
cache) plus platform sleep tools (shared `WakeupScheduler` backend); one
batch-shaped `step`; per-thread outputs aggregated in a `BTreeMap`.
- New `rap-receiver` binary replaces the drifted JS receiver (deleted): same
Function-URL contract, full parity with the daemon's conversion via
`infinity_agent_core::rap_callback` (user_choice accepted, multimodal and
`display_as` preserved, tagged synthetics honored, stable dedup IDs where the
wire has identity). CDK switches to a cargo-lambda `RustFunction`; the Function
URL is recreated on deploy, so persisted callback URLs must be re-subscribed.
Docs (`docs/docs/infinity-runtime`): the single "Rust API" page is replaced by two
sections, written in the Hydro docs register (definitional openings, bolded key
terms, cause-and-effect guarantees, code-first examples, admonitions for caveats):
"The Agent System API" (overview, building-a-system, running-locally, step-mode,
observers, including launcher mode) and "The Low-Level API" (overview,
history-manager, completion-loop). Cross-references updated repo-wide; `cargo doc`
is warning-free.
BREAKING CHANGE: `infinity-agent-core` removes `batch_processor` (`process_batch`,
`process_input_item`, `DisplayEvent`); `Thread` is no longer public (use
`AgentSystem::step` or `ThreadHandle`); `AgentSystem` is not `Clone`;
`LocalAgentSystem::start_with_handles` is now `start()` and custom observers use
`start_with_observer` (`LocalAgentSystem` gained a mode type parameter, default
`Handles`); `EventCollector::take` returns `(thread_id, event)` pairs; builder tools
are stored as `Rc<dyn Tool<M>>`; `Tool` gains defaulted `is_passive`;
`ToolContext`/builder lose `input_queue_arn`. `infinity-daemon` no longer exports
`sleep_tools`, `AgentMessage`, `thread_worker`, `agent_loop`, `Session`,
`InMemoryMessageSender`, `boot_rap_servers`, or `rap_tools`;
`SessionManager::switch_model` takes the requester's sender and returns
`Result<(), String>`; `send_input` takes `user_driven: bool`;
`SharedSessionManager` is `Rc<tokio::Mutex<...>>`. The CLI's `DisplayEvent` lives in
`infinity_agent_cli::display`.
Co-authored-by: Infinity 🤖 <infinity@hydro.run>
PR: #92
shadaj
force-pushed
the
sandbox-eb0ff46e-96e1-4d2d-8c0b-9c683fa4d1b9
branch
from
August 12, 2026 00:02
399a398 to
b822911
Compare
shadaj
added a commit
that referenced
this pull request
Aug 12, 2026
…on + lambda onto it
Introduce `infinity_agent_core::system`, a builder-based, object-oriented API for
running agents, rebuild both production embeddings on top of it, unify the parallel
implementations that had grown around the old architecture, and rewrite the
`infinity-runtime` docs around the new API.
Core (`infinity_agent_core::system`):
- `AgentSystemBuilder`: `new(stores, model, sender)` builds a step-mode `AgentSystem`
for serverless platforms; `new_local(stores, model)` builds a `LocalAgentSystem`
with an internal in-process queue (`ChannelSender`).
- Step mode's public surface is `AgentSystem::step(inputs, observer, defer)`: the
batch may span multiple threads (SQS FIFO batches can interleave message groups);
`step` partitions by `group_id`, applies the deferral policy per thread, joins the
per-thread slices concurrently (prepare → completion → history sync → observer
commit barrier → tool dispatch), and returns each thread's `StepOutcome`. `&mut
self` serializes calls per system; the internal `Thread` type is not public and
`AgentSystem` is not `Clone`, so two live in-memory views of one thread cannot
exist through the public API. Per-thread configuration resolves lazily on first
step so loading a thread never boots tool servers.
- `LocalAgentSystem` is mode-typed (`Handles` by default, `Launcher` after
`with_thread_launcher()`), with one `start()` per mode:
- Handles mode: `start()` returns a `RunningSystem` running an actor-system-style
runtime (router + one driver per active thread, input batching, `<interrupt>`
handling, deferral while a non-passive tool call is pending, auto-compaction
above 75% of the context window, race-free idle exit/respawn) with ack'd
`subscribe`, a `thread_exits` channel, and `thread_handle(id)` attaching to any
thread. `start_with_observer(factory)` swaps in a custom `ThreadObserver`.
- Launcher mode: `start()` returns a `LaunchingSystem`. `thread_builder()`
configures and launches new threads (generated IDs) with their own tools and
prompt, unioned onto the system-wide configuration (static or
`ThreadConfigSource`) via an internal `UnionConfigSource`; `thread_handle(id)`
re-attaches to existing threads only (launched in-process or with history).
Launch configurations are process-lifetime (not persisted).
- `ThreadHandle`: `replay()` plus an exactly-once `events` stream and input sending;
subscriptions survive driver respawns via a shared registry.
- `ThreadObserver`: synchronous `on_event(AgentEvent)` fan-out (exactly-once relative
to `on_subscribe` replays) plus awaited durability hooks (`on_user_choice_required`
/`_dismissed`, `on_commit` before tool dispatch). `EventCollector` buffers
`(thread_id, event)` pairs.
- `ModelSource` (per-round resolution; enables mid-session model switching),
`ThreadConfigSource` (per-thread tools/prompt/notifier; enables per-session RAP
servers and toolsets), `DeferQueue` (`InMemoryDeferQueue`/`NoDeferral`),
`Tool::is_passive()`, tokio sleep tools (`with_tokio_sleep_tools`).
- `batch_processor` deleted: `process_batch` had no production callers and its
semantics violated the documented durability barrier (swallowed sync errors, then
dispatched; emitted ResponseDone before sync). The documented low-level API is now
`HistoryManager` + `prepare_input`/`run_completion`/`execute_action`;
`execute_action_with_error_result` (the #88 error-fallback) lives in
`event_processor`. `DisplayEvent` moved out of core: `AgentEvent` is core's only
event vocabulary; the CLI keeps a local, non-generic `DisplayEvent` as its
terminal-rendering enum. All unique regression coverage ported.
- Shared slice internals: `event_processor::input_echo` is the single computation of
accepted-input echoes; `SyntheticKind::is_compaction_complete()`;
`InputMessage::user_text()`; driver's in-flight step modeled as
`InFlightStep { fut, cancel_tx }` with a consuming `cancel()`.
- `rap_callback` module: the RAP callback → `InputMessage` conversion (multimodal
content, display segments, tagged synthetics incl. subscription final/associative
flags, user_choice, oauth) lives in core with 11 unit tests; the daemon delegates.
- In-memory `ConversationStore`/`StateStore` in `infinity_agent_core::stores` with a
serializable snapshot/restore API; the daemon's stores are thin wrappers (extras +
JSON persistence) so the ancestor/compaction/dedup semantics exist in exactly one
place. `spawn_thread_with_id` supports deterministic IDs.
- Platform-neutral core: `input_queue_arn` removed from `ToolContext`/builder.
- All 20 daemon agent-loop tests migrated into core `system/tests.rs` with
byte-identical snapshots, plus new coverage: launcher tool/prompt unioning and
existing-only attachment, `ThreadHandle` streaming across tool-call rounds,
multi-group step batches, failed-dispatch fallback, OAuth surfacing, prepare-level
user-choice, handle respawn survival and pruning.
Daemon (`infinity-daemon`):
- One daemon-lifetime agent system replaces per-session agent loops; sessions are
root threads; a `thread_exits` watcher marks sessions idle and shuts down their
RAP servers when no keep-alive client is attached.
- `rap_servers.rs`: `ManagedRapServer` boots lazily per session via
`ThreadConfigSource` and reboots transparently after idle shutdown; `Up` caches
the manifest (fetch failure tears the boot down); MCP proxy tasks are owned
(stdio children `kill_on_drop`) but proxies stay up across idles since MCP servers
may be stateful; migration flows ride the same path (`MigrationServer`;
`boot_rap_servers`/`rap_tools.rs` deleted); config merging unified in
`config::load_merged_rap_config`.
- `DaemonObserver` implements the observer hooks; broadcasting unified in
`observer::broadcast_pruning`/`broadcast_to_thread` (fixing `handle_view_update`
never pruning dead subscribers); re-sent Connects replace rather than stack
subscriptions; replay rendering matches live rendering.
- `CatalogModelSource` resolves each thread's persisted model per round; fixed the
pre-existing double `ModelSwitched` delivery.
- `send_input` takes `user_driven: bool`; interior-mutability methods take `&self`.
- Web goldens `chat-image-result`/`chat-diff-result` regenerated (old goldens
captured an eager-boot layout artifact).
- Deleted: `session/thread_worker.rs`, `session/agent_loop.rs`, `sleep_tools.rs`,
`InMemoryMessageSender`, `Session`, `spawn_session`, `AgentMessage`.
Lambda (`infinity-agent-lambda`):
- Event handler rewritten on the new API: one system per invocation with a
`ThreadConfigSource` resolving each thread's RAP toolsets (DynamoDB manifest
cache) plus platform sleep tools (shared `WakeupScheduler` backend); one
batch-shaped `step`; per-thread outputs aggregated in a `BTreeMap`.
- New `rap-receiver` binary replaces the drifted JS receiver (deleted): same
Function-URL contract, full parity with the daemon's conversion via
`infinity_agent_core::rap_callback` (user_choice accepted, multimodal and
`display_as` preserved, tagged synthetics honored, stable dedup IDs where the
wire has identity). CDK switches to a cargo-lambda `RustFunction`; the Function
URL is recreated on deploy, so persisted callback URLs must be re-subscribed.
Docs (`docs/docs/infinity-runtime`): the single "Rust API" page is replaced by two
sections, written in the Hydro docs register (definitional openings, bolded key
terms, cause-and-effect guarantees, code-first examples, admonitions for caveats):
"The Agent System API" (overview, building-a-system, running-locally, step-mode,
observers, including launcher mode) and "The Low-Level API" (overview,
history-manager, completion-loop). Cross-references updated repo-wide; `cargo doc`
is warning-free.
BREAKING CHANGE: `infinity-agent-core` removes `batch_processor` (`process_batch`,
`process_input_item`, `DisplayEvent`); `Thread` is no longer public (use
`AgentSystem::step` or `ThreadHandle`); `AgentSystem` is not `Clone`;
`LocalAgentSystem::start_with_handles` is now `start()` and custom observers use
`start_with_observer` (`LocalAgentSystem` gained a mode type parameter, default
`Handles`); `EventCollector::take` returns `(thread_id, event)` pairs; builder tools
are stored as `Rc<dyn Tool<M>>`; `Tool` gains defaulted `is_passive`;
`ToolContext`/builder lose `input_queue_arn`. `infinity-daemon` no longer exports
`sleep_tools`, `AgentMessage`, `thread_worker`, `agent_loop`, `Session`,
`InMemoryMessageSender`, `boot_rap_servers`, or `rap_tools`;
`SessionManager::switch_model` takes the requester's sender and returns
`Result<(), String>`; `send_input` takes `user_driven: bool`;
`SharedSessionManager` is `Rc<tokio::Mutex<...>>`. The CLI's `DisplayEvent` lives in
`infinity_agent_cli::display`.
Co-authored-by: Infinity 🤖 <infinity@hydro.run>
PR: #92
shadaj
force-pushed
the
sandbox-eb0ff46e-96e1-4d2d-8c0b-9c683fa4d1b9
branch
from
August 12, 2026 00:04
b822911 to
d121304
Compare
shadaj
marked this pull request as ready for review
August 12, 2026 00:04
Member
Author
|
@MingweiSamuel I'm not sure how we want to go about reviewing this, most of the big blobs of code are unchanged from before, just moved around, but the new system builder, thread handle, etc APIs are new (and do have tests) |
shadaj
added a commit
that referenced
this pull request
Aug 12, 2026
…on + lambda onto it
Introduce `infinity_agent_core::system`, a builder-based, object-oriented API for
running agents, rebuild both production embeddings on top of it, unify the parallel
implementations that had grown around the old architecture, and rewrite the
`infinity-runtime` docs around the new API.
Core (`infinity_agent_core::system`, resident runtime under `system::local`):
- `AgentSystemBuilder`: `new(stores, model, sender)` builds a step-mode `AgentSystem`
for serverless platforms; `new_local(stores, model)` builds a `LocalAgentSystem`
with an internal in-process queue (`ChannelSender`).
- Step mode's public surface is `AgentSystem::step(inputs, observer, defer)`: the
batch may span multiple threads (SQS FIFO batches can interleave message groups);
`step` partitions by `group_id`, applies the deferral policy per thread, joins the
per-thread slices concurrently (prepare → completion → history sync → observer
commit barrier → tool dispatch), and returns each thread's `StepOutcome`. `&mut
self` serializes calls per system; the internal `Thread` type is not public and
`AgentSystem` is not `Clone`, so two live in-memory views of one thread cannot
exist through the public API. Per-thread configuration resolves lazily on first
step so loading a thread never boots tool servers.
- `LocalAgentSystem` is mode-typed (`Handles` by default, `Launcher` after
`with_thread_launcher()`), with one `start()` per mode:
- Handles mode: `start()` returns a `RunningSystem` running an actor-system-style
runtime (router + one driver per active thread, input batching, `<interrupt>`
handling, deferral while a non-passive tool call is pending, auto-compaction
above 75% of the context window, race-free idle exit/respawn) with ack'd
`subscribe`, a `thread_exits` channel, and `thread_handle(id)` attaching to any
thread. `start_with_observer(factory)` swaps in a custom `ThreadObserver`.
- Launcher mode: `start()` returns a `LaunchingSystem`. `thread_builder()`
configures and launches new threads (generated IDs) with their own tools and
prompt, unioned onto the system-wide configuration (static or
`ThreadConfigSource`); `thread_handle(id)` re-attaches to existing threads only.
Launch configurations are process-lifetime (not persisted).
- `ThreadHandle`: `replay()` plus an exactly-once `events` stream and input sending;
subscriptions survive driver respawns via a shared registry.
- `ThreadObserver`: synchronous `on_event(AgentEvent)` fan-out (exactly-once relative
to `on_subscribe` replays) plus awaited durability hooks (`on_user_choice_required`
/`_dismissed`, `on_commit` before tool dispatch). `EventCollector` buffers
`(thread_id, event)` pairs.
- `ModelSource` (per-round resolution; enables mid-session model switching),
`ThreadConfigSource` (per-thread tools/prompt/notifier; enables per-session RAP
servers and toolsets), `DeferQueue` (`InMemoryDeferQueue`/`NoDeferral`),
`Tool::is_passive()`, tokio sleep tools (`with_tokio_sleep_tools`).
- `batch_processor` deleted: `process_batch` had no production callers and its
semantics violated the documented durability barrier (swallowed sync errors, then
dispatched; emitted ResponseDone before sync). The documented low-level API is now
`HistoryManager` + `prepare_input`/`run_completion`/`execute_action`;
`execute_action_with_error_result` (the #88 error-fallback) lives in
`event_processor`. `DisplayEvent` moved out of core: `AgentEvent` is core's only
event vocabulary; the CLI keeps a local, non-generic `DisplayEvent` as its
terminal-rendering enum. All unique regression coverage ported.
- Shared slice internals: `event_processor::input_echo` is the single computation of
accepted-input echoes; `SyntheticKind::is_compaction_complete()`;
`InputMessage::user_text()`; driver's in-flight step modeled as
`InFlightStep { fut, cancel_tx }` with a consuming `cancel()`.
- `rap_callback` module: the RAP callback → `InputMessage` conversion (multimodal
content, display segments, tagged synthetics incl. subscription final/associative
flags, user_choice, oauth) lives in core with 11 unit tests; the daemon delegates.
- In-memory `ConversationStore`/`StateStore` in `infinity_agent_core::stores` with a
serializable snapshot/restore API; the daemon's stores are thin wrappers (extras +
JSON persistence) so the ancestor/compaction/dedup semantics exist in exactly one
place. `spawn_thread_with_id` supports deterministic IDs.
- Platform-neutral core: `input_queue_arn` removed from `ToolContext`/builder.
- All 20 daemon agent-loop tests migrated into core `system/tests.rs` with
byte-identical snapshots, plus new coverage: launcher tool/prompt unioning and
existing-only attachment, `ThreadHandle` streaming across tool-call rounds,
multi-group step batches, failed-dispatch fallback, OAuth surfacing, prepare-level
user-choice, handle respawn survival and pruning.
Daemon (`infinity-daemon`):
- One daemon-lifetime agent system replaces per-session agent loops; sessions are
root threads; a `thread_exits` watcher marks sessions idle and shuts down their
RAP servers when no keep-alive client is attached.
- `rap_servers.rs`: `ManagedRapServer` boots lazily per session via
`ThreadConfigSource` and reboots transparently after idle shutdown; `Up` caches
the manifest (fetch failure tears the boot down); MCP proxy tasks are owned
(stdio children `kill_on_drop`) but proxies stay up across idles since MCP servers
may be stateful; migration flows ride the same path (`MigrationServer`;
`boot_rap_servers`/`rap_tools.rs` deleted); config merging unified in
`config::load_merged_rap_config`.
- `DaemonObserver` implements the observer hooks; broadcasting unified in
`observer::broadcast_pruning`/`broadcast_to_thread` (fixing `handle_view_update`
never pruning dead subscribers); re-sent Connects replace rather than stack
subscriptions; replay rendering matches live rendering.
- `CatalogModelSource` resolves each thread's persisted model per round; fixed the
pre-existing double `ModelSwitched` delivery.
- `send_input` takes `user_driven: bool`; interior-mutability methods take `&self`.
- Web goldens `chat-image-result`/`chat-diff-result` regenerated (old goldens
captured an eager-boot layout artifact).
- Deleted: `session/thread_worker.rs`, `session/agent_loop.rs`, `sleep_tools.rs`,
`InMemoryMessageSender`, `Session`, `spawn_session`, `AgentMessage`.
Lambda (`infinity-agent-lambda`):
- Event handler rewritten on the new API: one system per invocation with a
`ThreadConfigSource` resolving each thread's RAP toolsets (DynamoDB manifest
cache) plus platform sleep tools (shared `WakeupScheduler` backend); one
batch-shaped `step`; per-thread outputs aggregated in a `BTreeMap`.
- New `rap-receiver` binary replaces the drifted JS receiver (deleted): same
Function-URL contract, full parity with the daemon's conversion via
`infinity_agent_core::rap_callback` (user_choice accepted, multimodal and
`display_as` preserved, tagged synthetics honored, stable dedup IDs where the
wire has identity). CDK switches to a cargo-lambda `RustFunction`; the Function
URL is recreated on deploy, so persisted callback URLs must be re-subscribed.
Docs (`docs/docs/infinity-runtime`): the single "Rust API" page is replaced by two
sections, written in the Hydro docs register (definitional openings, bolded key
terms, cause-and-effect guarantees, code-first examples, admonitions for caveats):
"The Agent System API" (overview, building-a-system, running-locally, step-mode,
observers, including launcher mode) and "The Low-Level API" (overview,
history-manager, completion-loop). Cross-references updated repo-wide; `cargo doc`
is warning-free.
BREAKING CHANGE: `infinity-agent-core` removes `batch_processor` (`process_batch`,
`process_input_item`, `DisplayEvent`); `Thread` is no longer public (use
`AgentSystem::step` or `ThreadHandle`); `AgentSystem` is not `Clone`;
`LocalAgentSystem::start_with_handles` is now `start()` and custom observers use
`start_with_observer` (`LocalAgentSystem` gained a mode type parameter, default
`Handles`); the resident-runtime types (`RunningSystem`, `SubscribeHandle`,
`ThreadHandle`, `HandleObserver`, `HandleSubscribeRequest`, `LaunchingSystem`,
`ThreadBuilder`, `ChannelSender`, `ChannelSendError`, `ActiveThreads`) live under
`system::local`; `EventCollector::take` returns `(thread_id, event)` pairs; builder
tools are stored as `Rc<dyn Tool<M>>`; `Tool` gains defaulted `is_passive`;
`ToolContext`/builder lose `input_queue_arn`. `infinity-daemon` no longer exports
`sleep_tools`, `AgentMessage`, `thread_worker`, `agent_loop`, `Session`,
`InMemoryMessageSender`, `boot_rap_servers`, or `rap_tools`;
`SessionManager::switch_model` takes the requester's sender and returns
`Result<(), String>`; `send_input` takes `user_driven: bool`;
`SharedSessionManager` is `Rc<tokio::Mutex<...>>`. The CLI's `DisplayEvent` lives in
`infinity_agent_cli::display`.
Co-authored-by: Infinity 🤖 <infinity@hydro.run>
PR: #92
shadaj
added a commit
that referenced
this pull request
Aug 12, 2026
…on + lambda onto it
Introduce `infinity_agent_core::system`, a builder-based, object-oriented API for
running agents, rebuild both production embeddings on top of it, unify the parallel
implementations that had grown around the old architecture, and rewrite the
`infinity-runtime` docs around the new API.
Core (`infinity_agent_core::system`, resident runtime under `system::local`):
- `AgentSystemBuilder`: `new(stores, model, sender)` builds a step-mode `AgentSystem`
for serverless platforms; `new_local(stores, model)` builds a `LocalAgentSystem`
with an internal in-process queue (`ChannelSender`).
- Step mode's public surface is `AgentSystem::step(inputs, observer, defer)`: the
batch may span multiple threads (SQS FIFO batches can interleave message groups);
`step` partitions by `group_id`, applies the deferral policy per thread, joins the
per-thread slices concurrently (prepare → completion → history sync → tool
dispatch), and returns each thread's `StepOutcome`. `&mut self` serializes calls
per system; the internal `Thread` type is not public and `AgentSystem` is not
`Clone`, so two live in-memory views of one thread cannot exist through the
public API. Per-thread configuration resolves lazily on first step so loading a
thread never boots tool servers.
- `LocalAgentSystem` is mode-typed (`Handles` by default, `Launcher` after
`with_thread_launcher()`), with one `start()` per mode:
- Handles mode: `start()` returns a `RunningSystem` running an actor-system-style
runtime (router + one driver per active thread, input batching, `<interrupt>`
handling, deferral while a non-passive tool call is pending, auto-compaction
above 75% of the context window, race-free idle exit/respawn) with ack'd
`subscribe`, a `thread_exits` channel, and `thread_handle(id)` attaching to any
thread. `start_with_observer(factory)` swaps in a custom `ThreadObserver`.
- Launcher mode: `start()` returns a `LaunchingSystem`. `thread_builder()`
configures and launches new threads (generated IDs) with their own tools,
prompt, and optionally their own `ModelSource`, unioned onto the system-wide
configuration; launch configurations attach to the launched (root) thread, so
spawned subagent threads inherit tools, prompt, and model via root resolution.
`thread_handle(id)` re-attaches to existing threads only (`None` means the
thread does not exist). Launch configurations are process-lifetime.
- Shutdown is ownership-based: `RunningSystem::shutdown(self)` (and
`LaunchingSystem::shutdown(self)`) cancels every driver, flushes in-flight turns,
and awaits the router. Because shutting down consumes the system, `&self` methods
are infallible: `thread_handle`/`launch` return handles directly, `subscribe`
resolves once installed, and `send`/`send_user_text` return `()`. Independent
handles that can outlive the system (`SubscribeHandle`, `ThreadHandle`) keep
fallible signatures.
- `ThreadHandle`: `replay()` plus an exactly-once `events` stream and input sending;
subscriptions survive driver respawns via a shared registry.
- `ThreadObserver`: synchronous `on_event(AgentEvent)` fan-out (exactly-once relative
to `on_subscribe` replays) plus awaited user-choice durability hooks
(`on_user_choice_required`/`_dismissed`). Tool dispatch happens strictly after
history sync, so turns are durable before their effects are observable.
`EventCollector` buffers `(thread_id, event)` pairs.
- `ModelSource` (per-round resolution; enables mid-session model switching),
`ThreadConfigSource` (per-thread tools/prompt/notifier; enables per-session RAP
servers and toolsets), `DeferQueue` (`InMemoryDeferQueue`/`NoDeferral`),
`Tool::is_passive()`, tokio sleep tools (`with_tokio_sleep_tools`).
- `batch_processor` deleted: `process_batch` had no production callers and its
semantics violated the documented durability barrier (swallowed sync errors, then
dispatched; emitted ResponseDone before sync). The documented low-level API is now
`HistoryManager` + `prepare_input`/`run_completion`/`execute_action`;
`execute_action_with_error_result` (the #88 error-fallback) lives in
`event_processor`. `DisplayEvent` moved out of core: `AgentEvent` is core's only
event vocabulary; the CLI keeps a local, non-generic `DisplayEvent` as its
terminal-rendering enum. All unique regression coverage ported.
- Shared slice internals: `event_processor::input_echo` is the single computation of
accepted-input echoes; `SyntheticKind::is_compaction_complete()`;
`InputMessage::user_text()`; driver's in-flight step modeled as
`InFlightStep { fut, cancel_tx }` with a consuming `cancel()`.
- `rap_callback` module: the RAP callback → `InputMessage` conversion (multimodal
content, display segments, tagged synthetics incl. subscription final/associative
flags, user_choice, oauth) lives in core with 11 unit tests; the daemon delegates.
- In-memory `ConversationStore`/`StateStore` in `infinity_agent_core::stores` with a
serializable snapshot/restore API; the daemon's stores are thin wrappers (extras +
JSON persistence) so the ancestor/compaction/dedup semantics exist in exactly one
place. `spawn_thread_with_id` supports deterministic IDs.
- Platform-neutral core: `input_queue_arn` removed from `ToolContext`/builder.
Unused refactor leftovers pruned: `ToolSet`/`VecToolSet`,
`ToolsConfig::{add_command, toolset_commands, mcp_servers, http_mcp_servers}`,
`HistoryManager::state_store()`.
- All 20 daemon agent-loop tests migrated into core `system/tests.rs` with
byte-identical snapshots, plus new coverage: launcher tool/prompt/model unioning
and child inheritance, existing-only attachment, `ThreadHandle` streaming across
tool-call rounds, multi-group step batches, failed-dispatch fallback, OAuth
surfacing, prepare-level user-choice, handle respawn survival and pruning.
Daemon (`infinity-daemon`):
- One daemon-lifetime agent system replaces per-session agent loops and the
restart-on-send path (`needs_restart` → `start_session`): idle threads respawn a
driver transparently instead. Sessions are root threads; a `thread_exits` watcher
marks sessions idle and shuts down their RAP servers when no keep-alive client is
attached.
- `rap_servers.rs`: RAP server lifecycle moves out of session start — boot happens
lazily when a thread's config first resolves (loading/replaying a thread never
boots servers), shutdown is driven by session idleness, and a shut-down server
reboots on the next tool interaction. `Up` caches the manifest (fetch failure
tears the boot down); MCP proxy tasks are owned (stdio children `kill_on_drop`)
but proxies stay up across idles since MCP servers may be stateful; migration
flows ride the same path (`MigrationServer`; `boot_rap_servers`/`rap_tools.rs`
deleted); config merging unified in `config::load_merged_rap_config`.
- `DaemonObserver` implements the observer hooks; broadcasting unified in
`observer::broadcast_pruning`/`broadcast_to_thread` (fixing `handle_view_update`
never pruning dead subscribers); re-sent Connects replace rather than stack
subscriptions; replay rendering matches live rendering.
- `CatalogModelSource` resolves each thread's persisted model per round; fixed the
pre-existing double `ModelSwitched` delivery.
- `send_input` takes `user_driven: bool`; interior-mutability methods take `&self`.
- Web goldens `chat-image-result`/`chat-diff-result` regenerated (old goldens
captured an eager-boot layout artifact).
- Deleted: `session/thread_worker.rs`, `session/agent_loop.rs`, `sleep_tools.rs`,
`InMemoryMessageSender`, `Session`, `spawn_session`, `AgentMessage`.
Lambda (`infinity-agent-lambda`):
- Event handler rewritten on the new API: one system per invocation with a
`ThreadConfigSource` resolving each thread's RAP toolsets (DynamoDB manifest
cache) plus platform sleep tools (shared `WakeupScheduler` backend); one
batch-shaped `step`; per-thread outputs aggregated in a `BTreeMap`.
- New `rap-receiver` binary replaces the drifted JS receiver (deleted): same
Function-URL contract, full parity with the daemon's conversion via
`infinity_agent_core::rap_callback` (user_choice accepted, multimodal and
`display_as` preserved, tagged synthetics honored, stable dedup IDs where the
wire has identity). CDK switches to a cargo-lambda `RustFunction`; the Function
URL is recreated on deploy, so persisted callback URLs must be re-subscribed.
Docs (`docs/docs/infinity-runtime`): the single "Rust API" page is replaced by two
sections, written in the Hydro docs register (definitional openings, bolded key
terms, cause-and-effect guarantees, code-first examples, admonitions for caveats):
"The Agent System API" (overview, building-a-system, running-locally, step-mode,
observers, including launcher mode) and "The Low-Level API" (overview,
history-manager, completion-loop). Cross-references updated repo-wide; `cargo doc`
is warning-free.
BREAKING CHANGE: `infinity-agent-core` removes `batch_processor` (`process_batch`,
`process_input_item`, `DisplayEvent`); `Thread` is no longer public (use
`AgentSystem::step` or `ThreadHandle`); `AgentSystem` is not `Clone`;
`LocalAgentSystem::start_with_handles` is now `start()` and custom observers use
`start_with_observer` (`LocalAgentSystem` gained a mode type parameter, default
`Handles`); the resident-runtime types (`RunningSystem`, `SubscribeHandle`,
`ThreadHandle`, `HandleObserver`, `HandleSubscribeRequest`, `LaunchingSystem`,
`ThreadBuilder`, `ChannelSender`, `ChannelSendError`, `ActiveThreads`) live under
`system::local`; `RunningSystem::begin_shutdown`/`task` are replaced by owned
`shutdown(self)`, and `RunningSystem::{send, send_user_text, subscribe,
thread_handle}` are infallible; `ThreadObserver` has no `on_commit` (persist in
`on_event` or externally; the turn is already durable before dispatch);
`EventCollector::take` returns `(thread_id, event)` pairs; builder tools are stored
as `Rc<dyn Tool<M>>`; `Tool` gains defaulted `is_passive`; `ToolContext`/builder
lose `input_queue_arn`; `ToolSet`/`VecToolSet` and `ToolsConfig::{add_command,
toolset_commands, mcp_servers, http_mcp_servers}` are removed. `infinity-daemon` no
longer exports `sleep_tools`, `AgentMessage`, `thread_worker`, `agent_loop`,
`Session`, `InMemoryMessageSender`, `boot_rap_servers`, or `rap_tools`;
`SessionManager::switch_model` takes the requester's sender and returns
`Result<(), String>`; `send_input` takes `user_driven: bool`;
`SharedSessionManager` is `Rc<tokio::Mutex<...>>`. The CLI's `DisplayEvent` lives in
`infinity_agent_cli::display`.
Co-authored-by: Infinity 🤖 <infinity@hydro.run>
PR: #92
shadaj
force-pushed
the
sandbox-eb0ff46e-96e1-4d2d-8c0b-9c683fa4d1b9
branch
from
August 12, 2026 18:31
4575d58 to
81b9046
Compare
shadaj
added a commit
that referenced
this pull request
Aug 12, 2026
…bridges Reorganize the agent-system documentation into a short default path from overview through an in-memory quickstart and local thread launching, followed by focused guides for dynamic configuration, RAP, MCP, custom tools, observers, step mode, and deeper engine customization. Merge subscription guidance into custom tools, document lifecycle behavior in the overview, and add validated contextual links and examples. Add lightweight infinity-mcp-bridge and infinity-rap-bridge crates so applications can connect protocol tools without depending on the daemon. Move MCP transport/session behavior and RAP callback conversion into those reusable libraries, retain thin daemon and Lambda adapters, and make callback server task lifetimes explicit. Update workspace integration and tests for the new bridge APIs. Validate the workspace with formatting, compilation, full tests, strict Clippy, rustdoc, and documentation link, anchor, fence, and style scans. Co-authored-by: Infinity 🤖 <infinity@hydro.run> PR: #92
shadaj
force-pushed
the
sandbox-eb0ff46e-96e1-4d2d-8c0b-9c683fa4d1b9
branch
from
August 12, 2026 18:34
81b9046 to
c705753
Compare
shadaj
added a commit
that referenced
this pull request
Aug 12, 2026
Remove the misleading dynamic-configuration recommendation from the overview and keep its default path focused on quickstart, local threads, and core tool integrations. Merge short navigation tails and API-note headings into the surrounding task-oriented narrative. Combine RAP callback reachability with custom callback routing, consolidate custom tool definition, result delivery, and registration, and unify observer event, durability, and live-attach guidance. Fold root-versus-child resolution and low-level builder notes into their parent sections, and expand the Lambda embedding into a substantive step-mode example. Verify all agent-system pages have balanced fences, valid local links and anchors, accepted style, Rust formatting, and no remaining undersized sections under the documentation audit. Co-authored-by: Infinity 🤖 <infinity@hydro.run> PR: #92
shadaj
force-pushed
the
sandbox-eb0ff46e-96e1-4d2d-8c0b-9c683fa4d1b9
branch
from
August 12, 2026 18:39
c705753 to
911edcb
Compare
shadaj
added a commit
that referenced
this pull request
Aug 12, 2026
Restore AgentSystemBuilder::without_builtin_tools as public API for systems that need a minimal or fully custom toolset. Stop re-exporting convert_callback from infinity-rap-bridge and make it crate-private, since prepare_callback is the consumed entry point. Audited internal support crates for further dead surface: LoadedToolset and from_manifest are used by the toolset loader's public signature and internals, SimpleHttpError and NoRapHttpError are required associated error types, and every HistoryManager method flagged by the scan is exercised by prepare_input and run_completion in production while forming the documented low-level API. No other removals were warranted. Workspace formatting, clippy with denied warnings, 125 tests across the four affected crates, and rustdoc for the bridges and core pass clean. Co-authored-by: Infinity 🤖 <infinity@hydro.run> PR: #92
shadaj
force-pushed
the
sandbox-eb0ff46e-96e1-4d2d-8c0b-9c683fa4d1b9
branch
from
August 12, 2026 21:53
911edcb to
3da95fe
Compare
shadaj
added a commit
that referenced
this pull request
Aug 12, 2026
Restore AgentSystemBuilder::without_builtin_tools as public API for systems that need a minimal or fully custom toolset. Stop re-exporting convert_callback from infinity-rap-bridge and make it crate-private, since prepare_callback is the consumed entry point. Audited internal support crates for further dead surface: LoadedToolset and from_manifest are used by the toolset loader's public signature and internals, SimpleHttpError and NoRapHttpError are required associated error types, and every HistoryManager method flagged by the scan is exercised by prepare_input and run_completion in production while forming the documented low-level API. No other removals were warranted. Workspace formatting, clippy with denied warnings, 125 tests across the four affected crates, and rustdoc for the bridges and core pass clean. Co-authored-by: Infinity 🤖 <infinity@hydro.run> PR: #92
shadaj
force-pushed
the
sandbox-eb0ff46e-96e1-4d2d-8c0b-9c683fa4d1b9
branch
from
August 12, 2026 21:59
3da95fe to
5eacc60
Compare
shadaj
added a commit
that referenced
this pull request
Aug 12, 2026
Regenerate the THIRD-PARTY file with cargo-about and the npm license checkers so the workspace list includes the new infinity-mcp-bridge and infinity-rap-bridge crates. No new third-party dependencies were introduced, so the notice content is otherwise unchanged. Co-authored-by: Infinity 🤖 <infinity@hydro.run> PR: #92
shadaj
force-pushed
the
sandbox-eb0ff46e-96e1-4d2d-8c0b-9c683fa4d1b9
branch
from
August 12, 2026 22:17
5eacc60 to
588e9bf
Compare
shadaj
added a commit
that referenced
this pull request
Aug 12, 2026
Regenerate the THIRD-PARTY file with cargo-about and the npm license checkers so the workspace list includes the new infinity-mcp-bridge and infinity-rap-bridge crates. No new third-party dependencies were introduced, so the notice content is otherwise unchanged. Co-authored-by: Infinity 🤖 <infinity@hydro.run> PR: #92
shadaj
force-pushed
the
sandbox-eb0ff46e-96e1-4d2d-8c0b-9c683fa4d1b9
branch
from
August 12, 2026 22:21
588e9bf to
883f241
Compare
shadaj
added a commit
that referenced
this pull request
Aug 12, 2026
Regenerate the THIRD-PARTY file with cargo-about and the npm license checkers so the workspace list includes the new infinity-mcp-bridge and infinity-rap-bridge crates. No new third-party dependencies were introduced, so the notice content is otherwise unchanged. Co-authored-by: Infinity 🤖 <infinity@hydro.run> PR: #92
shadaj
force-pushed
the
sandbox-eb0ff46e-96e1-4d2d-8c0b-9c683fa4d1b9
branch
from
August 12, 2026 22:31
883f241 to
31cd016
Compare
shadaj
added a commit
that referenced
this pull request
Aug 12, 2026
Remove the definitions parity test and the toolset naming test, which became self-comparisons once the local Tool adapters were rebuilt as wrappers over the same McpToolDefinition values, along with the operation parse test subsumed by dispatch routing and the descriptor conversion test that restated From field copies. The behavioral tests remain: lazy one-time transport connection with request recording, and dispatch routing including the guarantee that unknown operations never reach the server. The PR description no longer cites the removed parity test. Co-authored-by: Infinity 🤖 <infinity@hydro.run> PR: #92
shadaj
force-pushed
the
sandbox-eb0ff46e-96e1-4d2d-8c0b-9c683fa4d1b9
branch
from
August 12, 2026 23:40
31cd016 to
d2cf08a
Compare
shadaj
added a commit
that referenced
this pull request
Aug 13, 2026
Remove the definitions parity test and the toolset naming test, which became self-comparisons once the local Tool adapters were rebuilt as wrappers over the same McpToolDefinition values, along with the operation parse test subsumed by dispatch routing and the descriptor conversion test that restated From field copies. The behavioral tests remain: lazy one-time transport connection with request recording, and dispatch routing including the guarantee that unknown operations never reach the server. The PR description no longer cites the removed parity test. Co-authored-by: Infinity 🤖 <infinity@hydro.run> PR: #92
shadaj
force-pushed
the
sandbox-eb0ff46e-96e1-4d2d-8c0b-9c683fa4d1b9
branch
from
August 13, 2026 01:05
d2cf08a to
a112560
Compare
shadaj
added a commit
that referenced
this pull request
Aug 13, 2026
Remove the definitions parity test and the toolset naming test, which became self-comparisons once the local Tool adapters were rebuilt as wrappers over the same McpToolDefinition values, along with the operation parse test subsumed by dispatch routing and the descriptor conversion test that restated From field copies. The behavioral tests remain: lazy one-time transport connection with request recording, and dispatch routing including the guarantee that unknown operations never reach the server. The PR description no longer cites the removed parity test. Co-authored-by: Infinity 🤖 <infinity@hydro.run> PR: #92
shadaj
force-pushed
the
sandbox-eb0ff46e-96e1-4d2d-8c0b-9c683fa4d1b9
branch
from
August 13, 2026 01:08
a112560 to
a8c3438
Compare
shadaj
added a commit
that referenced
this pull request
Aug 13, 2026
Move the daemon and Lambda runtimes onto the shared infinity-agent-core engine, including routing, lifecycle, state-store, observer, step-processing, and shutdown behavior. Split MCP and RAP integrations into reusable bridge crates and retain migrated daemon fidelity coverage. Defer the additive ThreadHandle and launcher APIs to the stacked follow-up revision. Co-authored-by: Infinity 🤖 <infinity@hydro.run> PR: #92
shadaj
added a commit
that referenced
this pull request
Aug 13, 2026
Add thread handles for sending input and streaming observer events across completion rounds. Introduce launcher mode, per-thread builders, and inherited tool, prompt, model, and configuration sources for dynamically launched threads. Document the high-level agent-system workflow, local execution, custom tools, dynamic configuration, observers, step mode, MCP servers, RAP servers, and engine customization. BREAKING CHANGE: The public local agent-system builder now selects handle or launcher operation modes and exposes the corresponding running-system types. Co-authored-by: Infinity 🤖 <infinity@hydro.run> PR: #92
shadaj
force-pushed
the
sandbox-eb0ff46e-96e1-4d2d-8c0b-9c683fa4d1b9
branch
from
August 13, 2026 02:51
a8c3438 to
81a6208
Compare
shadaj
added a commit
that referenced
this pull request
Aug 13, 2026
Add thread handles for sending input and streaming observer events across completion rounds. Introduce launcher mode, per-thread builders, and inherited tool, prompt, model, and configuration sources for dynamically launched threads. Document the high-level agent-system workflow, local execution, custom tools, dynamic configuration, observers, step mode, MCP servers, RAP servers, and engine customization. BREAKING CHANGE: The public local agent-system builder now selects handle or launcher operation modes and exposes the corresponding running-system types. Co-authored-by: Infinity 🤖 <infinity@hydro.run> PR: #92
shadaj
changed the base branch from
main
to
sandbox-0f86f15a-4591-435a-bff7-27a45f33e143
August 13, 2026 02:58
shadaj
force-pushed
the
sandbox-eb0ff46e-96e1-4d2d-8c0b-9c683fa4d1b9
branch
from
August 13, 2026 02:58
81a6208 to
17ccfd6
Compare
Keep PR 1 focused on the shared engine and production embeddings by deferring static builder conveniences, local MCP/RAP tool-set adapters, and usage guides to PR 2. Preserve migrated behavior with private test configuration sources and remove the unused null observer. In PR 2, replace the RAP bridge's late-bound sender mutex and duplicate tool wrapper with the shared core RapTool. Make callback ownership explicit by requiring a callback URL during discovery and attaching RapCallbackBridge directly to the running system sender. Update the application guides and pull-request review boundaries accordingly. Co-authored-by: Infinity 🤖 <infinity@hydro.run> PR: #92
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Stack
This is PR 2 of 2, based on the shared-engine refactor in #96. Review #96 first; this diff contains the application-facing API, local protocol adapters, and documentation.
Summary
Add ergonomic local agent-system APIs on top of the engine extracted in #96:
ThreadHandles for sending inputs and streaming events;ThreadBuilderfor per-thread tools, prompts, and models;McpToolSetandRapToolSetadapters;The daemon, Lambda, stores, driver, thread pipeline, admission, lifecycle behavior, and embedding-oriented bridge foundations are reviewed in #96.
Review guide
ThreadHandleand handle modesystem/local/handle.rsadds a channel-based observer and registry:RunningSystem::thread_handle(id)attaches to a thread and returns a handle that can send inputs and receive its event stream.Tests cover sending and receiving, streaming across tool-call rounds, driver respawn, and pruning dropped handles.
Launcher mode
system/local/launch.rsadds:LocalAgentSystem::with_thread_launcher()andLaunchingSystem;ThreadBuilderfor launching a new root thread with its own tools, system prompt, and model;Launch configuration is registered before the seed message is sent.
UnionConfigSourceandUnionModelSourceresolve entries by the thread's root ID, so child threads inherit their parent's launch-specific tools, prompt, and model while retaining system-wide configuration.Tests cover tool/prompt union, child inheritance, per-thread model selection, and attach-only behavior.
Builder typestate and conveniences
LocalAgentSystemgains explicit operation modes:Handlesis the default andstart()returnsRunningSystemwith built-in handles.Launcheris selected withwith_thread_launcher()andstart()returnsLaunchingSystem.start_with_observerremains available for embeddings that provide their own observer.AgentSystemBuilderalso gains statictool,tools,extra_system_prompt, andrap_notifierhelpers for applications that do not need a customThreadConfigSource.Local MCP and RAP adapters
McpToolSetexposes a lazy stdio or Streamable HTTP MCP client as local list/invoke tools while sharing metadata and dispatch with the daemon-facingMcpClient.RapToolSetdiscovers manifest tools and returns the shared coreRapToolimplementation. It takes an explicit callback URL rather than capturing the most recently invoked system sender.RapCallbackBridgeis bound separately and explicitly attached withserve_into(system.sender()), making callback ownership and view-update handling unambiguous. There is no late-bound sender mutex or destination switching.Documentation
Adds The Agent System API section:
It also adds focused low-level guides for the history manager and completion loop, and updates existing runtime pages to direct application authors toward the new API.
Validation
cargo fmt --all -- --checkcargo clippy --workspace --all-targets -- -D warningscargo test --workspace, including doc testsBreaking changes
Relative to #96:
LocalAgentSystemgains a mode type parameter, defaulting toHandles.start(); custom observers continue to usestart_with_observer.ThreadHandle,HandleObserver,HandleSubscribeRequest,LaunchingSystem, andThreadBuilderare exported undersystem::local.RapToolgains an optional callback URL used by local RAP tool sets.No daemon behavior changes are introduced in this PR.