feat(infinity-agent-core)!: add high-level agent system API - #92
Conversation
…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
5689aff to
7a10b86
Compare
Deploying infinity with
|
| Latest commit: |
4e360d9
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://1a1b408b.infinity-dc7.pages.dev |
| Branch Preview URL: | https://sandbox-eb0ff46e-96e1-4d2d-8.infinity-dc7.pages.dev |
…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
7a10b86 to
32bcb39
Compare
|
Oh god |
…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
32bcb39 to
723ade3
Compare
…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
723ade3 to
794035c
Compare
…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
794035c to
608cb0f
Compare
…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
608cb0f to
fc4b88c
Compare
…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
fc4b88c to
6840c02
Compare
…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
6840c02 to
ccb90b9
Compare
…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
ccb90b9 to
ec1b0d4
Compare
…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
ec1b0d4 to
399a398
Compare
…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
399a398 to
b822911
Compare
…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
b822911 to
d121304
Compare
|
@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) |
…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
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
c705753 to
911edcb
Compare
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
911edcb to
3da95fe
Compare
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
3da95fe to
5eacc60
Compare
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
5eacc60 to
588e9bf
Compare
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
588e9bf to
883f241
Compare
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
883f241 to
31cd016
Compare
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
31cd016 to
d2cf08a
Compare
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
d2cf08a to
a112560
Compare
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
a112560 to
a8c3438
Compare
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
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
a8c3438 to
81a6208
Compare
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
81a6208 to
17ccfd6
Compare
|
Whats the differences between this (higher level?) API and the APIs used by the daemon (cli?) directly? |
There was a problem hiding this comment.
Pull request overview
This PR adds an application-facing “agent system” API on top of the shared engine (from #96), including local execution conveniences (handles + launcher mode), protocol adapters (local MCP and RAP toolsets), and a substantial documentation restructure to guide embedding authors toward the new high-level API.
Changes:
- Introduces local-system ergonomics in
infinity-agent-core(channel-backedThreadHandles and launcher mode viaThreadBuilder). - Adds local adapters for MCP and RAP tool servers (
McpToolSet,RapToolSet/RapCallbackBridge) and updates RAP tool callback behavior to support an explicit callback URL. - Reorganizes and expands Infinity Runtime docs into “Agent System API” and “Low-Level API” sections, updating cross-references accordingly.
Reviewed changes
Copilot reviewed 37 out of 38 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| docs/docs/infinity-runtime/threading.md | Sidebar ordering update. |
| docs/docs/infinity-runtime/rust-api.md | Removes the legacy “Rust API” page (replaced by new sections). |
| docs/docs/infinity-runtime/overview.md | Updates embedding guidance to point to the new agent-system docs and clarifies positioning. |
| docs/docs/infinity-runtime/model-providers.md | Sidebar ordering and minor wording adjustments. |
| docs/docs/infinity-runtime/low-level/overview.md | Adds low-level API overview and crate/trait mapping. |
| docs/docs/infinity-runtime/low-level/history-manager.md | Adds history manager guide. |
| docs/docs/infinity-runtime/low-level/completion-loop.md | Adds completion-loop guide (prepare/run/dispatch composition). |
| docs/docs/infinity-runtime/low-level/category.json | Adds nav category for low-level API docs. |
| docs/docs/infinity-runtime/deploying-on-lambda.mdx | Updates embedded-runtime references to agent-system docs and refreshes callback behavior notes. |
| docs/docs/infinity-runtime/built-in-tools.md | Updates references and clarifies sleep tooling behavior. |
| docs/docs/infinity-runtime/architecture.md | Updates terminology and references to new step/agent-system docs. |
| docs/docs/infinity-runtime/agent-systems/step-mode.md | Adds step-mode guide for platform-scheduled embeddings. |
| docs/docs/infinity-runtime/agent-systems/running-locally.md | Adds local driver + launcher workflow documentation. |
| docs/docs/infinity-runtime/agent-systems/rap-servers.md | Documents local RAP toolset discovery and callback bridging. |
| docs/docs/infinity-runtime/agent-systems/overview.md | Adds top-level agent-system overview and execution-mode comparison. |
| docs/docs/infinity-runtime/agent-systems/observers.md | Documents observer responsibilities, attach semantics, and durability hooks. |
| docs/docs/infinity-runtime/agent-systems/mcp-servers.md | Documents McpToolSet usage for stdio/HTTP MCP servers. |
| docs/docs/infinity-runtime/agent-systems/dynamic-configuration.md | Adds durable per-thread config/model resolution docs. |
| docs/docs/infinity-runtime/agent-systems/customizing-the-engine.md | Documents extension points for persistence/model/scheduling customization. |
| docs/docs/infinity-runtime/agent-systems/custom-tools.md | Adds guide for implementing local Rust tools (including subscription streams). |
| docs/docs/infinity-runtime/agent-systems/building-a-system.md | Adds quickstart for building/running a local agent system. |
| docs/docs/infinity-runtime/agent-systems/category.json | Adds nav category for agent-system docs. |
| crates/infinity-rap-bridge/src/lib.rs | Adds RapToolSet for local tool registration and clarifies callback bridging. |
| crates/infinity-mcp-bridge/src/lib.rs | Adds McpToolSet exposing MCP servers as local tools via shared McpClient. |
| crates/infinity-mcp-bridge/Cargo.toml | Adds new dependencies to support local-tool adapter implementation. |
| crates/infinity-daemon/src/session/tests.rs | Updates daemon session tests to use new builder static configuration helpers. |
| crates/infinity-agent-lambda/src/event_handler.rs | Adjusts RAP tool construction for the new optional callback URL field. |
| crates/infinity-agent-core/src/tools/rap_tool.rs | Adds optional per-tool callback URL override and derives Clone. |
| crates/infinity-agent-core/src/system/test_support.rs | Updates/extends test support helpers for handle + launcher systems. |
| crates/infinity-agent-core/src/system/observer.rs | Expands docs for the live-attach subscribe hook semantics. |
| crates/infinity-agent-core/src/system/mod.rs | Adds crate-level example and exports new mode markers/types. |
| crates/infinity-agent-core/src/system/local/router.rs | Refines local start APIs and mode bounds. |
| crates/infinity-agent-core/src/system/local/mod.rs | Exports handle + launcher modules/types. |
| crates/infinity-agent-core/src/system/local/launch.rs | Implements launcher mode and root-based config/model inheritance. |
| crates/infinity-agent-core/src/system/local/handle.rs | Implements ThreadHandle and built-in handle observer/registry. |
| crates/infinity-agent-core/src/system/config.rs | Makes StaticThreadConfig public and aligns it with new builder helpers. |
| crates/infinity-agent-core/src/system/builder.rs | Adds static config helpers, launcher plumbing, mode markers, and sender accessors. |
| Cargo.lock | Records dependency graph changes. |
Suppressed comments (3)
docs/docs/infinity-runtime/low-level/overview.md:57
- This paragraph still describes
group_idas a separate argument tosend_to_input_queue, but the API now takes onlydedup_idand reads the target thread fromInputMessage::group_id.
docs/docs/infinity-runtime/agent-systems/custom-tools.md:234 - This subscription example uses the old
send_to_input_queue(started, group_id, dedup_id)signature. The current API issend_to_input_queue(message, dedup_id).
context
.message_sender
.send_to_input_queue(started, &context.group_id, &id)
.await?;
docs/docs/infinity-runtime/agent-systems/custom-tools.md:260
- This send path still passes
group_idas an argument, butsend_to_input_queueonly takes the deduplication ID now (the thread ID is inevent.group_id).
if let Err(error) = sender
.send_to_input_queue(event, &group_id, &dedup_id)
.await
{
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
The main difference is that the new high-level API allows for directly controlling individual agent instances using handles, whereas the daemon implementation uses its own |
MingweiSamuel
left a comment
There was a problem hiding this comment.
How confident are you that this isn't over-engineered? It seems like there are a lot more code paths/features than are strictly necessary, at a glance
| /// through it; all other threads fall through to the inner source. | ||
| pub(crate) struct UnionModelSource<C: ConversationStore, M: InputSender> { | ||
| pub(crate) inner: Box<dyn ModelSource>, | ||
| pub(crate) registry: Rc<LaunchRegistry<M>>, |
There was a problem hiding this comment.
Seems awkward that LaunchRegistry is Rc wrapped, could it either be Rc<RefCell<LaunchRegistry>> or just have threads: Rc<RefCell<HashMap<String, LaunchConfig>>> inside the struct?
There was a problem hiding this comment.
Seems like every place it is used it is wrapped in Rc
There was a problem hiding this comment.
Putting the RC inside would match HandleRegistry
| /// The handle subscribers attached to each thread. One registry is shared by | ||
| /// every [`HandleObserver`] the system creates, so a subscription survives | ||
| /// its thread's driver idling out and respawning. | ||
| #[derive(Clone, Default)] | ||
| struct HandleRegistry { | ||
| subscribers: Rc<RefCell<HashMap<String, Vec<mpsc::UnboundedSender<AgentEvent>>>>>, | ||
| } |
There was a problem hiding this comment.
The HandleRegistry struct doesn't seem to really do anything? Could just make HandleObserver have subscriber_registry: Rc<RefCell<HashMap<String, Vec<mpsc::UnboundedSender<AgentEvent>>>>> or however you want to name it. Will probably have to silence type complexity
| conversation_store: C, | ||
| state_store: S, | ||
| model: Box<dyn ModelSource>, | ||
| tools: Vec<Rc<dyn Tool<M>>>, |
There was a problem hiding this comment.
Is the Rc needed? Could we just use Box<dyn _>?
|
|
||
| /// The tools, prompt, and model a thread was launched with. | ||
| struct LaunchConfig<M: InputSender> { | ||
| tools: Vec<Rc<dyn Tool<M>>>, |
There was a problem hiding this comment.
Does tools really need Rc?
| H: HttpClient, | ||
| { | ||
| system: &'a LaunchingSystem<C, S, H>, | ||
| tools: Vec<Rc<dyn Tool<ChannelSender>>>, |
| let mut config = self.inner.resolve(thread_id).await?; | ||
| let root_id = root_thread_id(&self.conversation_store, thread_id).await?; | ||
| if let Some(local) = self.registry.threads.borrow().get(&root_id) { | ||
| config.tools.extend(local.tools.iter().cloned()); |
There was a problem hiding this comment.
I guess this is (the only? place) where Rc::clone is needed for tools
| pub struct Handles; | ||
|
|
||
| /// Marker for launcher mode; see [`Handles`]. | ||
| pub struct Launcher; |
There was a problem hiding this comment.
use empty enum if these are just marker types, to prevent accidental instantiation
| pub struct Handles; | |
| /// Marker for launcher mode; see [`Handles`]. | |
| pub struct Launcher; | |
| pub enum Handles {} | |
| /// Marker for launcher mode; see [`Handles`]. | |
| pub enum Launche {} |
| This quickstart creates a local agent with in-memory state, launches one conversation thread, and streams its events. You need a Tokio `LocalSet` because local agent tasks do not require `Send`. | ||
|
|
||
| ```rust | ||
| use infinity_agent_core::stores::{InMemoryConversationStore, InMemoryStateStore}; | ||
| use infinity_agent_core::system::{AgentEvent, AgentSystemBuilder, StaticModel}; | ||
| use tokio::task::LocalSet; | ||
|
|
||
| #[tokio::main] |
There was a problem hiding this comment.
#[tokio::main(flavor = "current_thread")] can be used to automatically set up the local set, I believe
* Remove the local-system mode markers, `with_thread_launcher`, and the built-in `RunningSystem::thread_handle` path that could create agents from arbitrary IDs * Make `build_local().start()` return the thread-builder-based `LaunchingSystem` directly * Keep `start_with_observer` as the lower-level embedding API and confine arbitrary thread routing to that observer-owned layer * Add fallible existing-thread attachment through `LaunchingSystem::thread_handle`, using `ConversationStore::thread_exists` and returning `Result<Option<ThreadHandle>, _>` * Keep the handle observer and subscription request private as implementation details of `ThreadBuilder` and existing-thread attachment * Update tests and documentation to create all new local threads through `thread_builder().launch()` and remove implicit-creation examples BREAKING CHANGE: `LocalAgentSystem::start` now returns `LaunchingSystem`, `with_thread_launcher`, the local-system mode marker types, and `RunningSystem::thread_handle` are removed. Existing-thread attachment now returns `Result<Option<ThreadHandle>, ConversationStore::Error>`. Co-authored-by: Infinity 🤖 <infinity@hydro.run> PR: #92
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.