diff --git a/docs/tinyagents-migration-spec.md b/docs/tinyagents-migration-spec.md index 334de2a27..19b57b5de 100644 --- a/docs/tinyagents-migration-spec.md +++ b/docs/tinyagents-migration-spec.md @@ -3,14 +3,17 @@ Status: draft migration backlog TinyAgents source reviewed: `tinyhumansai/tinyagents` `origin/main` at -`8f226f1`, crate version `1.1.0`. +`8f226f1`, crate version `1.1.0`. Current OpenHuman dependency in this checkout +is `tinyagents = "1.2"`. -OpenHuman already depends on `tinyagents = "1.1"` and already routes the live +OpenHuman already depends on `tinyagents = "1.2"` and already routes the live agent turn through `src/openhuman/tinyagents/`. This spec is not a proposal to add TinyAgents. It is a todo list for moving the rest of OpenHuman's generic agent runtime behavior onto TinyAgents primitives while keeping OpenHuman-owned product semantics in OpenHuman. +Current inventory snapshot: [`tinyagents-harness-migration-audit.md`](tinyagents-harness-migration-audit.md). + ## Goal Use TinyAgents as the generic runtime for: @@ -73,7 +76,7 @@ OpenHuman Rust core: Already done or partially done: -- `Cargo.toml` pins `tinyagents = "1.1"` with default features only. +- `Cargo.toml` pins `tinyagents = "1.2"` with default features only. - `src/openhuman/tinyagents/mod.rs` registers OpenHuman `Provider` and `Tool` adapters on `tinyagents::harness::runtime::AgentHarness`. - `ProviderModel` maps OpenHuman `ChatRequest`/`ChatResponse` into @@ -222,7 +225,7 @@ OpenHuman needs restart-safe SQL/JSON ledgers. `memory`, `subconscious`, etc.) can resolve to a TinyAgents model entry while retaining OpenHuman provider strings and config compatibility. -- [ ] Translate OpenHuman provider capability data into TinyAgents model profiles. +- [~] Translate OpenHuman provider capability data into TinyAgents model profiles. - OpenHuman files: `src/openhuman/inference/provider/traits.rs`, `src/openhuman/inference/provider/factory.rs`, `docs/inference-provider-catalog.md`. @@ -231,6 +234,17 @@ OpenHuman needs restart-safe SQL/JSON ledgers. - Acceptance: context window, tool calling, streaming, vision, structured output, reasoning, local/cloud source, and provider-family metadata are available before dispatch. + - **Partial:** every `ProviderModel` registered by the shared runner now + carries a crate `ModelProfile` built at construction from the provider's + canonical capability accessors — tool calling (+parallel), vision + (`modalities.image_in`), streaming, local/remote source — plus the + runner-threaded token limits (`with_context_window` → `max_input_tokens`, + output cap → `max_output_tokens`). `ChatModel::profile()` returns it, so + the crate's pre-dispatch validation and structured-output strategy see real + capabilities. Remaining: structured-output/JSON-schema/reasoning flags + (no OpenHuman capability source yet), release/status metadata, and a + registry-level model *catalog* (ties into the workload-route registry item + above). - [ ] Move model fallback and retry policy to TinyAgents policy/middleware. - OpenHuman files: `src/openhuman/inference/provider/reliable.rs`, @@ -579,7 +593,7 @@ assessment, and migration coverage are complete. - Candidate outcome: replace dozens of boilerplate default `graph.rs` files with registry defaults, keeping files only for agents with custom graphs. -- [~] Audit stale architecture references to removed in-house graph/loop code. +- [x] Audit stale architecture references to removed in-house graph/loop code. - Current files: `gitbooks/developing/architecture/agent-harness.md`, `src/openhuman/context/README.md`. - Candidate stale names: `src/openhuman/agent_graph/`, `GraphBlueprint`, @@ -594,9 +608,16 @@ assessment, and migration coverage are complete. section as HISTORICAL-removed (strong inline callout pointing at the live tinyagents surfaces); fixed `context/README.md` "Used by" line that still referenced the deleted `reduce_before_call`/`ProviderSummarizer`/ - `SegmentRecapSummarizer`/`unified_compaction_enabled`. Remaining: a sweep of - code doc-comments across many `.rs` files that still name `run_turn_engine`, - `run_tool_call_loop`, `tool_loop.rs` (comments only — no behavior). + `SegmentRecapSummarizer`/`unified_compaction_enabled`. **Sweep completed:** + every code doc-comment that described `run_turn_engine`/`run_tool_call_loop`/ + `tool_loop.rs` as *current* behavior (≈20 sites across 15 files: tools, + security, tokenjuice, triage, orchestration steering, task-local contexts, + cron, host_runtime, event-bus example, test-file headers) now points at the + live tinyagents surfaces; intentionally-historical "legacy X was removed / + parity with" notes were kept. Domain READMEs (tools, tokenjuice, approval) + fixed too — the tokenjuice one now records that `compact_tool_output` lost + its only production caller with the retired loop (re-wiring it as an + `after_tool` middleware is an open follow-up). - [ ] Audit `src/openhuman/context/{pipeline,guard,microcompact}.rs`. - Current role: context stats/session-memory bookkeeping plus older @@ -649,7 +670,7 @@ assessment, and migration coverage are complete. - Candidate outcome: document why each fanout stays as `join_all` or move it to `run_parallel_fanout` / graph `Send`. -- [ ] Audit tool registry comments and docs that still describe retired +- [x] Audit tool registry comments and docs that still describe retired direct-loop behavior. - Current files: `src/openhuman/tools/traits.rs`, `src/openhuman/tools/README.md`, @@ -659,6 +680,9 @@ assessment, and migration coverage are complete. - Candidate outcome: update comments to describe the TinyAgents execution path and delete references to the retired serial `harness::tool_loop` dispatcher once no code path uses it. + - **Done:** `tools/traits.rs` concurrency note and `tools/README.md` "Used + by" now describe the tinyagents execution path (`SharedToolAdapter` / + `ToolPolicyMiddleware`); `session/turn/tools.rs` had no stale references. ## Phase 11 - Testing And Conformance diff --git a/src/core/event_bus/native_request.rs b/src/core/event_bus/native_request.rs index 12835ddbd..d35da7c17 100644 --- a/src/core/event_bus/native_request.rs +++ b/src/core/event_bus/native_request.rs @@ -31,7 +31,7 @@ //! register_native_global::( //! "agent.run_turn", //! |req| async move { -//! let text = run_tool_call_loop(/* ... */).await +//! let text = run_agent_turn(/* ... */).await //! .map_err(|e| e.to_string())?; //! Ok(AgentTurnResponse::new(text)) //! }, diff --git a/src/openhuman/agent/harness/harness_gap_tests.rs b/src/openhuman/agent/harness/harness_gap_tests.rs index abfb9d5a0..34f5e361b 100644 --- a/src/openhuman/agent/harness/harness_gap_tests.rs +++ b/src/openhuman/agent/harness/harness_gap_tests.rs @@ -3,7 +3,9 @@ //! These tests cover paths that were missing from the existing `*_tests.rs` //! co-located files as identified by a coverage gap analysis: //! -//! 1. Full user→LLM→tool→result→final turn cycle with `run_tool_call_loop`. +//! 1. Full user→LLM→tool→result→final turn cycle — now covered by the +//! tinyagents route's tests (`src/openhuman/tinyagents/tests.rs`), which +//! exercise `run_turn_via_tinyagents_shared` end to end. //! 2. `MaxIterationsExceeded` downcasts to the typed `AgentError` variant. //! 3. `visible_tool_names` whitelist: tools outside the set are treated as unknown. //! 4. `ContextGuard` surfaces `ContextExhausted` and aborts the loop. diff --git a/src/openhuman/agent/harness/session/mod.rs b/src/openhuman/agent/harness/session/mod.rs index 02e75b1e1..80aa0f5b1 100644 --- a/src/openhuman/agent/harness/session/mod.rs +++ b/src/openhuman/agent/harness/session/mod.rs @@ -37,6 +37,6 @@ mod tests; pub use types::{Agent, AgentBuilder}; // Re-export the duplicate-tool-spec guard for sibling harness modules -// (`tool_loop`, `subagent_runner`) so all three provider call sites +// (`session::runtime`, `subagent_runner`) so all provider call sites // share one tested implementation. pub(crate) use builder::dedup_visible_tool_specs; diff --git a/src/openhuman/agent/harness/subagent_runner/ops/runner.rs b/src/openhuman/agent/harness/subagent_runner/ops/runner.rs index 09a28316e..3650a9978 100644 --- a/src/openhuman/agent/harness/subagent_runner/ops/runner.rs +++ b/src/openhuman/agent/harness/subagent_runner/ops/runner.rs @@ -109,16 +109,17 @@ pub async fn run_subagent( // Unconditionally heap-allocate the entire run_subagent body so // every caller doesn't have to carry this future's state inline. // Tools that delegate run inside the parent agent's already-deep - // `run_turn_engine` poll, so the parent's stack would otherwise pile - // (parent engine state + dispatch_subagent state + run_subagent's - // wrapper state + run_typed_mode state + child engine state) onto - // tokio's 2 MiB worker stack and abort with "thread + // turn poll (the boxed tinyagents harness drive future in + // `run_turn_via_tinyagents_shared`), so the parent's stack would + // otherwise pile (parent turn state + dispatch_subagent state + + // run_subagent's wrapper state + run_typed_mode state + child turn + // state) onto tokio's 2 MiB worker stack and abort with "thread // 'tokio-rt-worker' has overflowed its stack, fatal runtime error: // stack overflow" — observed at `[subagent_runner] dispatching // agent_id=researcher ...` in the `chat-harness-subagent` Playwright - // lane crash. The inner `Box::pin`s around `run_typed_mode` / - // `run_inner_loop` / child `run_turn_engine` further chunk the - // child's state so a single sub-agent run can't blow the stack either. + // lane crash. The inner `Box::pin`s around `run_typed_mode` and the + // child's tinyagents drive future further chunk the child's state so + // a single sub-agent run can't blow the stack either. Box::pin(async move { let parent = current_parent().ok_or(SubagentRunError::NoParentContext)?; let task_id = options diff --git a/src/openhuman/agent/harness/subagent_runner/ops_tests.rs b/src/openhuman/agent/harness/subagent_runner/ops_tests.rs index 4f7b18a53..b0c0633c2 100644 --- a/src/openhuman/agent/harness/subagent_runner/ops_tests.rs +++ b/src/openhuman/agent/harness/subagent_runner/ops_tests.rs @@ -561,10 +561,10 @@ async fn capped_no_progress_subagent_returns_incomplete_status() { async fn run_queue_steer_lands_in_subagent_history() { // End-to-end proof that flipping the subagent loop's run-queue arg from // `None` to `Some(queue)` wires steering all the way through: a message - // pushed to the queue before the run is drained by the inner - // `run_turn_engine` and appears as a `[User steering message]:` user turn - // in the exact request sent to the provider. This is the mechanism behind - // the `steer_subagent` tool. + // pushed to the queue before the run is drained by the steering forwarder + // in the child's turn (`run_turn_via_tinyagents_shared`) and appears as a + // `[User steering message]:` user turn in the exact request sent to the + // provider. This is the mechanism behind the `steer_subagent` tool. let provider = ScriptedProvider::new(vec![text_response("acknowledged")]); let parent = make_parent(provider.clone(), vec![stub("file_read")]); let def = make_def_named_tools(&[]); @@ -1514,8 +1514,8 @@ fn nested_subagent_dispatch_runs_on_a_constrained_worker_stack() { let outcome = outcome.expect( "nested run_subagent must complete on a 1 MiB worker stack — \ a stack overflow here means the recursion boundary in \ - `run_typed_mode` regressed (see `Box::pin` callsites around \ - `run_inner_loop` and `run_turn_engine`).", + `run_typed_mode` regressed (see the `Box::pin` callsites around \ + `run_typed_mode` and the child's tinyagents drive future).", ); assert!( outcome.output.contains("inner-final"), diff --git a/src/openhuman/agent/harness/turn_attachments_context.rs b/src/openhuman/agent/harness/turn_attachments_context.rs index b752d3e10..61e73ee31 100644 --- a/src/openhuman/agent/harness/turn_attachments_context.rs +++ b/src/openhuman/agent/harness/turn_attachments_context.rs @@ -13,9 +13,9 @@ //! rehydrates the image from the on-disk sidecar. //! //! Mirrors [`super::model_vision_context`]. Scoped around the orchestrator's -//! `run_turn_engine` call; [`current_turn_image_placeholders`] returns an empty -//! vec when no scope is active (CLI / direct invocation / tests) — strictly -//! additive. +//! turn future (`run_turn_via_tinyagents_shared`); +//! [`current_turn_image_placeholders`] returns an empty vec when no scope is +//! active (CLI / direct invocation / tests) — strictly additive. tokio::task_local! { /// Image-attachment placeholder tokens from the current turn's user message. @@ -30,8 +30,8 @@ pub fn current_turn_image_placeholders() -> Vec { } /// Run `future` with `placeholders` installed as the current turn's image -/// placeholders. Intended call site is around the orchestrator's -/// `run_turn_engine` invocation. +/// placeholders. Intended call site is around the orchestrator's turn +/// (`run_turn_via_tinyagents_shared`) invocation. pub async fn with_current_turn_image_placeholders(placeholders: Vec, future: F) -> R where F: std::future::Future, diff --git a/src/openhuman/agent/harness/turn_subagent_usage.rs b/src/openhuman/agent/harness/turn_subagent_usage.rs index ba32b9832..49565f82d 100644 --- a/src/openhuman/agent/harness/turn_subagent_usage.rs +++ b/src/openhuman/agent/harness/turn_subagent_usage.rs @@ -9,10 +9,11 @@ //! cost tracker. //! //! This module installs an [`Arc>>`] as a -//! task-local around the parent's `run_turn_engine` call. Synchronous -//! delegations (`spawn_subagent`) run inline on the same tokio task, so the -//! sub-agent runner can [`record_subagent_usage`] its totals into the active -//! collector. After the engine returns, the parent [`drain`]s the collector to: +//! task-local around the parent's turn future (the +//! `run_turn_via_tinyagents_shared` drive). Synchronous delegations +//! (`spawn_subagent`) run inline on the same tokio task, so the sub-agent +//! runner can [`record_subagent_usage`] its totals into the active collector. +//! After the turn returns, the parent [`drain`]s the collector to: //! //! 1. fold child tokens + USD into the turn's cumulative meters, and //! 2. attribute per-child spend for the `chat_done` breakdown (hover detail). @@ -64,7 +65,8 @@ pub type TurnSubagentUsage = Arc>>; tokio::task_local! { /// Active per-turn sub-agent usage collector, installed around the parent's - /// `run_turn_engine` call. Absent outside a turn scope. + /// turn future (`run_turn_via_tinyagents_shared`). Absent outside a turn + /// scope. static TURN_SUBAGENT_USAGE: TurnSubagentUsage; } @@ -115,7 +117,7 @@ pub fn record_subagent_usage(task_id: &str, agent_id: &str, usage: SubagentUsage /// Run `future` with a fresh sub-agent usage collector installed, returning both /// the future's output and the gathered per-child entries. Intended call site is -/// around the parent agent's `run_turn_engine` invocation. +/// around the parent agent's turn (`run_turn_via_tinyagents_shared`) invocation. pub async fn with_turn_collector(future: F) -> (R, Vec) where F: std::future::Future, diff --git a/src/openhuman/agent/host_runtime.rs b/src/openhuman/agent/host_runtime.rs index 51c5d15bd..956b6e2b3 100644 --- a/src/openhuman/agent/host_runtime.rs +++ b/src/openhuman/agent/host_runtime.rs @@ -88,7 +88,8 @@ impl RuntimeAdapter for NativeRuntime { // `pip install … | tail`) surfaces as a non-zero exit instead of // being masked by the last stage's success. Without it the harness // records the call as successful and the repeated-failure circuit - // breaker (see tool_loop.rs) never trips, so the agent loops on a + // breaker (`RepeatedToolFailureMiddleware`, tinyagents/middleware.rs) + // never trips, so the agent loops on a // command that is silently failing. `/bin/sh` is dash on // Debian/Ubuntu and rejects `set -o pipefail`, so this is gated on // bash actually being present; otherwise we fall back to plain sh. diff --git a/src/openhuman/agent/triage/evaluator.rs b/src/openhuman/agent/triage/evaluator.rs index 0d86b71dc..009776211 100644 --- a/src/openhuman/agent/triage/evaluator.rs +++ b/src/openhuman/agent/triage/evaluator.rs @@ -20,13 +20,13 @@ //! are treated like retryable cloud failures: retry once, then fall //! through to local / Deferred. //! -//! ## Why `run_tool_call_loop` doesn't care about `tools_registry = []` +//! ## Why the turn path doesn't care about `tools_registry = []` //! //! The triage agent has `named = []` in its TOML (zero tools). The -//! `run_tool_call_loop` implementation in -//! `src/openhuman/agent/harness/tool_loop.rs` handles an empty registry -//! by just doing a plain `chat_with_history` under the hood — no tool -//! schemas are sent to the backend. +//! tinyagents-backed turn path (`run_turn_via_tinyagents_shared` in +//! `src/openhuman/tinyagents/mod.rs`) handles an empty registry by simply +//! sending no tool schemas to the backend — the turn degrades to a plain +//! chat completion. use std::future::Future; use std::sync::Arc; diff --git a/src/openhuman/agent/triage/routing.rs b/src/openhuman/agent/triage/routing.rs index bd3056a16..eb6ff161e 100644 --- a/src/openhuman/agent/triage/routing.rs +++ b/src/openhuman/agent/triage/routing.rs @@ -24,8 +24,8 @@ pub struct ResolvedProvider { /// Provider name token — always `"openhuman"` (remote backend). /// Kept for telemetry / observability compat with the previous two-path design. pub provider_name: String, - /// Model identifier — the concrete string `run_tool_call_loop` - /// will hand to the provider. + /// Model identifier — the concrete string the turn + /// (`run_turn_via_tinyagents_shared`) will hand to the provider. pub model: String, /// Always `false` — local AI is never used for triage. /// Preserved so existing telemetry subscribers that read this field do not diff --git a/src/openhuman/agent_orchestration/running_subagents.rs b/src/openhuman/agent_orchestration/running_subagents.rs index 22a54d42c..c958610d4 100644 --- a/src/openhuman/agent_orchestration/running_subagents.rs +++ b/src/openhuman/agent_orchestration/running_subagents.rs @@ -6,8 +6,9 @@ //! closes both gaps. //! //! Each running async sub-agent registers, keyed by its `task_id`, with: -//! - an `Arc` — the same steering channel the inner `run_turn_engine` -//! drains at iteration boundaries, so `steer_subagent` can inject a message; +//! - an `Arc` — the same steering channel the steering forwarder in +//! `run_turn_via_tinyagents_shared` drains mid-turn, so `steer_subagent` can +//! inject a message; //! - a `watch::Receiver` — so `wait_subagent` can block until the //! child reaches a terminal status; //! - an `AbortHandle` — kept for a future `close_agent` tool. @@ -302,8 +303,9 @@ pub enum SteerError { AlreadyDone, } -/// Inject a message into a running sub-agent's steering queue. The child's -/// `run_turn_engine` drains it at the next iteration boundary. +/// Inject a message into a running sub-agent's steering queue. The steering +/// forwarder in the child's `run_turn_via_tinyagents_shared` turn drains it +/// mid-flight. pub async fn steer( task_id: &str, parent_session: &str, diff --git a/src/openhuman/agent_orchestration/tools/steer_subagent.rs b/src/openhuman/agent_orchestration/tools/steer_subagent.rs index cb08ee03e..13fdd3005 100644 --- a/src/openhuman/agent_orchestration/tools/steer_subagent.rs +++ b/src/openhuman/agent_orchestration/tools/steer_subagent.rs @@ -2,8 +2,9 @@ //! //! Pairs with `spawn_async_subagent`: that tool returns a `task_id` for a child //! running in the background. `steer_subagent` pushes a message into that child's -//! steering queue, which its inner `run_turn_engine` drains at the next iteration -//! boundary — so the parent can redirect or feed data to a running sub-agent +//! steering queue, which the steering forwarder in the child's turn +//! (`run_turn_via_tinyagents_shared`) drains mid-flight — so the parent can +//! redirect or feed data to a running sub-agent //! without waiting for it to finish or restarting it. Mirrors Codex `send_input`. use crate::openhuman::agent::harness::fork_context::current_parent; diff --git a/src/openhuman/approval/README.md b/src/openhuman/approval/README.md index 23268d27c..18c0b48d7 100644 --- a/src/openhuman/approval/README.md +++ b/src/openhuman/approval/README.md @@ -79,7 +79,7 @@ SQLite DB at `{workspace_dir}/approval/approval.db`, table `pending_approvals` ( - `src/core/jsonrpc.rs` — installs the global gate (`ApprovalGate::init_global`) at startup; wires the approval RPCs. - `src/core/all.rs` — registers the controller schemas. -- `src/openhuman/agent/harness/tool_loop.rs` (+ subagent_runner) — routes external-effect tool calls through the gate before `execute()`. +- `src/openhuman/tinyagents/middleware.rs` (`ApprovalSecurityMiddleware`, a `wrap_tool` middleware on every turn path) — routes external-effect tool calls through the gate before `execute()` and records the terminal audit row. - `src/openhuman/channels/providers/web.rs` — sets `APPROVAL_CHAT_CONTEXT`, hosts `ApprovalSurfaceSubscriber`, and routes typed yes/no replies to `approval_decide`. - `src/openhuman/channels/proactive.rs`, `src/openhuman/agent/triage/escalation.rs`, `src/openhuman/tools/impl/system/install_tool.rs`, `src/openhuman/wallet/execution.rs` — interact with the gate / approval types. diff --git a/src/openhuman/cron/scheduler.rs b/src/openhuman/cron/scheduler.rs index b31cdf592..1f2dc04a4 100644 --- a/src/openhuman/cron/scheduler.rs +++ b/src/openhuman/cron/scheduler.rs @@ -317,8 +317,9 @@ pub async fn execute_job_now(config: &Config, job: &CronJob) -> (bool, String) { /// after a single JWT lapse, every retries-exhausted capture pointing at a /// problem the user can only fix from the UI. /// -/// The right move is the same halt-on-first-occurrence pattern as -/// `agent::harness::tool_loop::BACKEND_USER_STATE_MARKER` (#3334): the +/// The right move is the same halt-on-first-occurrence pattern as the +/// legacy tool loop's `BACKEND_USER_STATE_MARKER` convention (#3334, the +/// loop itself was retired in the tinyagents migration, #4249): the /// condition is global and retries can't recover it, so we stop after the /// first attempt. Skipping the `report_error` call too is correct because /// the existing classifier diff --git a/src/openhuman/inference/provider/billing_error.rs b/src/openhuman/inference/provider/billing_error.rs index a118aff3c..5c8b18c43 100644 --- a/src/openhuman/inference/provider/billing_error.rs +++ b/src/openhuman/inference/provider/billing_error.rs @@ -83,7 +83,7 @@ mod tests { /// Verbatim Anthropic BYO out-of-credits 400 body (Sentry TAURI-RUST-4MM). /// Direct provider — "anthropic API error", not the managed "OpenHuman API /// error". The classifier feeds the emit-site `classify_expected_error` - /// demotion, the agent `tool_loop` gate, the `web_errors` net, the + /// demotion, the agent turn's billing gate, the `web_errors` net, the /// `is_budget_event` before_send filter, AND the cron billing-halt, so /// pinning the exact wire body makes an Anthropic phrasing drift fail CI /// rather than silently re-flood Sentry (3793 events leaked before this). diff --git a/src/openhuman/security/policy/types.rs b/src/openhuman/security/policy/types.rs index bbb0d9c5a..1b938b100 100644 --- a/src/openhuman/security/policy/types.rs +++ b/src/openhuman/security/policy/types.rs @@ -10,9 +10,10 @@ use tokio::sync::OnceCell; /// rejection: the identical `(tool, args)` call can never succeed in the /// current tier (read-only blocking a write, a forbidden/credential path, a /// disallowed high-risk or hidden-execution command, an off-allowlist command). -/// The agent harness ([`crate::openhuman::agent::harness::tool_loop`]) detects -/// this and halts on the **first verbatim repeat** rather than reiterating a -/// provably-futile call. Kept short and bracketed so it survives the +/// The agent harness's repeated-failure middleware +/// ([`crate::openhuman::tinyagents::middleware::RepeatedToolFailureMiddleware`]) +/// detects this and halts on the **first verbatim repeat** rather than +/// reiterating a provably-futile call. Kept short and bracketed so it survives the /// `Error: …` wrapping the tool layer adds and is easy to grep in logs. pub const POLICY_BLOCKED_MARKER: &str = "[policy-blocked]"; diff --git a/src/openhuman/tokenjuice/README.md b/src/openhuman/tokenjuice/README.md index 103b0efa1..ae55a963c 100644 --- a/src/openhuman/tokenjuice/README.md +++ b/src/openhuman/tokenjuice/README.md @@ -61,7 +61,7 @@ This module is **fully self-contained within `openhuman`** — it has no `use cr ## Used by -- `src/openhuman/agent/harness/tool_loop.rs` — the only consumer. Calls `crate::openhuman::tokenjuice::compact_tool_output(&call.name, Some(&call.arguments), &scrubbed, Some(0))` after a successful tool call (post credential-scrub) to compact tool output before it is fed back to the model. +- **Currently unwired on the live turn path.** The only production consumer of `compact_tool_output` was the retired in-house `agent/harness/tool_loop.rs` (removed in the tinyagents migration, issue #4249); the tinyagents route caps/summarizes tool output via `ToolOutputMiddleware` instead. Re-expressing tokenjuice compaction as an `after_tool` middleware on that path is the outstanding follow-up (see `docs/tinyagents-migration-spec.md`). ## Notes / gotchas diff --git a/src/openhuman/tokenjuice/savings.rs b/src/openhuman/tokenjuice/savings.rs index 4106011cc..17bbc0c75 100644 --- a/src/openhuman/tokenjuice/savings.rs +++ b/src/openhuman/tokenjuice/savings.rs @@ -94,8 +94,8 @@ fn state() -> &'static Mutex { } tokio::task_local! { - /// The model actually running the current turn/sub-agent, scoped by the - /// agent loop around `run_turn_engine` (mirrors + /// The model actually running the current turn/sub-agent, scoped around + /// the tinyagents turn (`run_turn_via_tinyagents_shared`) (mirrors /// [`crate::openhuman::agent::harness::model_vision_context`]). When set, /// compaction savings are priced against *this* model instead of the /// process-global configured default (issue #4122). Unset ⇒ fall back to @@ -105,9 +105,9 @@ tokio::task_local! { } /// Run `future` with `model` installed as the per-turn attribution model used -/// to price compaction savings. Intended call site is around each -/// `run_turn_engine` invocation, alongside the other per-turn `*_context` -/// scopes (issue #4122). +/// to price compaction savings. Intended call site is around each turn's +/// `run_turn_via_tinyagents_shared` invocation, alongside the other per-turn +/// `*_context` scopes (issue #4122). pub async fn with_turn_model(model: String, future: F) -> R where F: std::future::Future, diff --git a/src/openhuman/tools/README.md b/src/openhuman/tools/README.md index 84584880e..522d426cb 100644 --- a/src/openhuman/tools/README.md +++ b/src/openhuman/tools/README.md @@ -105,7 +105,7 @@ None. No `store.rs`; the module holds no persisted state. Tools that persist (me ## Used by - `src/core/all.rs` — registers the `tools` RPC controllers + schemas. -- `openhuman::agent` harness (`session/builder`, `tool_loop`, `dispatcher`, `subagent_runner`, `agent/tools/*`) — primary consumer; builds and runs the registry, applies the `ToolPolicy`. +- `openhuman::agent` harness (`session/builder`, `dispatcher`, `subagent_runner`, `agent/tools/*`) and the `openhuman::tinyagents` seam (`SharedToolAdapter`, `ToolPolicyMiddleware`) — primary consumers; build the registry and execute/police tools on the tinyagents harness path. - `openhuman::channels`, `openhuman::routing`, `openhuman::inference::provider` — build tool sets / clean schemas per provider. - `openhuman::agent_tool_policy`, `openhuman::approval` — read tool metadata (category, external-effect) for policy/approval decisions. - `openhuman::tool_registry`, `openhuman::runtime_node`, `openhuman::mcp_server` — registry/exposure consumers. diff --git a/src/openhuman/tools/impl/filesystem/file_write.rs b/src/openhuman/tools/impl/filesystem/file_write.rs index 44b8fa6e6..eac9a46fd 100644 --- a/src/openhuman/tools/impl/filesystem/file_write.rs +++ b/src/openhuman/tools/impl/filesystem/file_write.rs @@ -390,7 +390,8 @@ mod tests { // The readonly block must carry the hard-reject marker so the agent // harness recognizes it and halts on a verbatim repeat instead of // grinding. Ties this tool's literal to the marker const — the - // const→detector half is covered by tool_loop's guard tests. + // const→detector half lives in `RepeatedToolFailureMiddleware` and is + // covered by its tests (`src/openhuman/tinyagents/middleware.rs`). assert!( result .output() diff --git a/src/openhuman/tools/impl/system/command_output.rs b/src/openhuman/tools/impl/system/command_output.rs index c50c45cd8..d835f7f57 100644 --- a/src/openhuman/tools/impl/system/command_output.rs +++ b/src/openhuman/tools/impl/system/command_output.rs @@ -14,10 +14,10 @@ //! (command / dependency not found) or a `126` (permission denied — often a //! sandbox restriction) from a generic `1`, so it could not recognise an //! un-retryable wall and re-ran the identical command. The harness -//! repeated-failure circuit breaker (`RepeatFailureGuard`, see -//! `agent/harness/tool_loop.rs`) still bounds that loop, but only after a -//! few wasted iterations and with a generic halt message, because the -//! root-cause signal had already been thrown away. +//! repeated-failure circuit breaker (`RepeatedToolFailureMiddleware`, see +//! `src/openhuman/tinyagents/middleware.rs`) still bounds that loop, but +//! only after a few wasted iterations and with a generic halt message, +//! because the root-cause signal had already been thrown away. //! //! This module surfaces the exit code AND both streams on failure, and appends a //! short hint for the well-known dependency/sandbox exit codes so the agent can diff --git a/src/openhuman/tools/ops_tests.rs b/src/openhuman/tools/ops_tests.rs index c1c6a8b65..b18759914 100644 --- a/src/openhuman/tools/ops_tests.rs +++ b/src/openhuman/tools/ops_tests.rs @@ -1506,7 +1506,8 @@ async fn all_tools_executes_stock_and_twilio_family_against_fake_backend() { /// Every acting tool gates on `can_act()` and returns its own read-only refusal /// string. Each of those must carry [`POLICY_BLOCKED_MARKER`] so the agent /// harness recognizes the block as a hard reject and halts on a verbatim repeat -/// (see `agent::harness::tool_loop::hard_reject_kind`). This pins every tool's +/// (see the marker detection in +/// `tinyagents::middleware::RepeatedToolFailureMiddleware`). This pins every tool's /// literal to the marker const — drift between them fails here rather than /// silently letting the agent grind on a doomed call. Args are the minimum /// needed to reach the `can_act()` check in each tool. diff --git a/src/openhuman/tools/traits.rs b/src/openhuman/tools/traits.rs index d7b13ee42..4e87d3bf7 100644 --- a/src/openhuman/tools/traits.rs +++ b/src/openhuman/tools/traits.rs @@ -348,12 +348,12 @@ pub trait Tool: Send + Sync { /// `cat` invocations and reject parallel `npm install`s) — most /// tools will ignore it. /// - /// **Wiring note:** the parallel dispatcher in - /// `harness::tool_loop` currently runs tool calls serially - /// regardless of this flag. Annotating tools is still load- - /// bearing: it lets the dispatch refactor land without - /// coordinating with every tool author. See the parallel-tool - /// dispatch follow-up issue. + /// **Wiring note:** the tinyagents harness loop (see + /// `crate::openhuman::tinyagents::tools`) currently executes tool + /// calls serially regardless of this flag. Annotating tools is + /// still load-bearing: it lets a parallel-dispatch refactor land + /// without coordinating with every tool author. See the + /// parallel-tool dispatch follow-up issue. fn is_concurrency_safe(&self, _args: &serde_json::Value) -> bool { false } diff --git a/src/openhuman/workflows/e2e_plumbing_tests.rs b/src/openhuman/workflows/e2e_plumbing_tests.rs index ee224ff6d..eff0b428f 100644 --- a/src/openhuman/workflows/e2e_plumbing_tests.rs +++ b/src/openhuman/workflows/e2e_plumbing_tests.rs @@ -44,7 +44,8 @@ use crate::openhuman::workflows::run_log; // ── Mock LLM ───────────────────────────────────────────────────────────── // Minimal scripted provider: pops queued ChatResponses in order. Mirrors the -// one in `agent/harness/tool_loop_tests.rs` (kept local so this file is +// scripted providers in other harness test files (e.g. +// `agent/harness/subagent_runner/ops_tests.rs`; kept local so this file is // self-contained). struct ScriptedProvider { responses: Mutex>>,