mirror of
https://github.com/tinyhumansai/openhuman.git
synced 2026-07-27 21:08:00 +00:00
docs(agent): sweep retired-loop names out of current-behavior comments (#4249)
Phase 10: every code comment / domain README that still described run_turn_engine, run_tool_call_loop, or harness/tool_loop.rs as the CURRENT implementation now points at the live tinyagents surfaces (run_turn_via_tinyagents_shared, RepeatedToolFailureMiddleware, ApprovalSecurityMiddleware, the steering forwarder, SharedToolAdapter / ToolPolicyMiddleware). Intentionally-historical parity notes kept verbatim. Comments only — zero behavior changes. Notable finding recorded in tokenjuice/README.md: compact_tool_output lost its only production caller with the retired tool_loop; re-wiring it as an after_tool middleware is an open spec follow-up.
This commit is contained in:
@@ -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
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
//! register_native_global::<AgentTurnRequest, AgentTurnResponse, _, _>(
|
||||
//! "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))
|
||||
//! },
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"),
|
||||
|
||||
@@ -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<String> {
|
||||
}
|
||||
|
||||
/// 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<F, R>(placeholders: Vec<String>, future: F) -> R
|
||||
where
|
||||
F: std::future::Future<Output = R>,
|
||||
|
||||
@@ -9,10 +9,11 @@
|
||||
//! cost tracker.
|
||||
//!
|
||||
//! This module installs an [`Arc<Mutex<Vec<SubagentUsageEntry>>>`] 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<Mutex<Vec<SubagentUsageEntry>>>;
|
||||
|
||||
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<F, R>(future: F) -> (R, Vec<SubagentUsageEntry>)
|
||||
where
|
||||
F: std::future::Future<Output = R>,
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -6,8 +6,9 @@
|
||||
//! closes both gaps.
|
||||
//!
|
||||
//! Each running async sub-agent registers, keyed by its `task_id`, with:
|
||||
//! - an `Arc<RunQueue>` — the same steering channel the inner `run_turn_engine`
|
||||
//! drains at iteration boundaries, so `steer_subagent` can inject a message;
|
||||
//! - an `Arc<RunQueue>` — 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<SubagentStatus>` — 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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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).
|
||||
|
||||
@@ -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]";
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -94,8 +94,8 @@ fn state() -> &'static Mutex<State> {
|
||||
}
|
||||
|
||||
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<F, R>(model: String, future: F) -> R
|
||||
where
|
||||
F: std::future::Future<Output = R>,
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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<Vec<anyhow::Result<ChatResponse>>>,
|
||||
|
||||
Reference in New Issue
Block a user