mirror of
https://github.com/outbackdingo/optimclaw.git
synced 2026-08-26 15:40:18 +00:00
* feat: complete multi-tenant isolation — per-user budgets, model selection, heartbeat cycling Finishes the remaining isolation work from phases 2–4 of #59: Phase 2 (DB scoping): Fix /status and /list commands to use _for_user DB variants instead of global queries that leaked cross-user job data. Phase 3 (Runtime isolation): Per-user workspace in routine engine's spawn_fire so lightweight routines run in the correct user context. Per-user daily cost tracking in CostGuard with configurable budget via MAX_COST_PER_USER_PER_DAY_CENTS. Multi-user heartbeat that cycles through all users with routines, auto-detected from GATEWAY_USER_TOKENS. Phase 4 (Provider/tools): Per-user model selection via preferred_model setting — looked up from SettingsStore on first iteration, threaded through ReasoningContext.model_override to CompletionRequest. Works with providers that support per-request model overrides (NearAI). Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * fix: use selected_model setting key to match /model command persistence The dispatcher was reading "preferred_model" but the /model command (merged from staging) persists to "selected_model". Since set_setting is already per-user scoped, using the same key makes /model work as the per-user model override in multi-tenant mode. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * fix: heartbeat hygiene, /model multi-tenant guard, RigAdapter model override Three follow-up fixes for multi-tenant isolation: 1. Multi-user heartbeat now runs memory hygiene per user before each heartbeat check, matching single-user heartbeat behavior. 2. /model command in multi-tenant mode only persists to per-user settings (selected_model) without calling set_model() on the shared LlmProvider. The per-request model_override in the dispatcher reads from the same setting. Added multi_tenant flag to AgentConfig (auto-detected from GATEWAY_USER_TOKENS). 3. RigAdapter now supports per-request model overrides by injecting the model name into rig-core's additional_params. OpenAI/Anthropic/Ollama API servers use last-key-wins for duplicate JSON keys, so the override takes effect via serde's flatten serialization order. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * fix: address PR review — cost model attribution, heartbeat concurrency, pruning Fixes from review comments on #1614: - Cost tracking now uses the override model name (not active_model_name) when a per-user model override is active, for accurate attribution. - Multi-user heartbeat runs per-user checks concurrently via JoinSet instead of sequentially, preventing one slow user from blocking others. - Per-user failure counts tracked independently; users exceeding max_failures are skipped (matching single-user semantics). - per_user_daily_cost HashMap pruned on day rollover to prevent unbounded growth in long-lived deployments. - Doc comment fixed: says "routines" not "active routines". Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * fix: /status ownership, model persistence scoping, heartbeat robustness Addresses second round of PR review on #1614: - /status <job_id> DB path now validates job.user_id == requesting user before returning data (was missing ownership check, security fix). - persist_selected_model takes user_id param instead of owner_id, and skips .env/TOML writes in multi-tenant mode (these are shared global files). handle_system_command now receives user_id from caller. - JoinSet collection handles Err(JoinError) explicitly instead of silently dropping panicked tasks. - Notification forwarder extracts owner_id from response metadata in multi-tenant mode for per-user routing instead of broadcasting to the agent owner. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * fix: cost pricing, fire_manual workspace, heartbeat concurrency cap Round 3 review fixes: - Cost tracking passes None for cost_per_token when model override is active, letting CostGuard look up pricing by model name instead of using the default provider's rates (serrrfirat). - fire_manual() now uses per-user workspace, matching spawn_fire() pattern (serrrfirat). - Removed MULTI_TENANT env var — multi-tenant mode is auto-detected solely from GATEWAY_USER_TOKENS presence (serrrfirat + Copilot). - Multi-user heartbeat capped at 8 concurrent tasks to avoid flooding the LLM provider (serrrfirat + Copilot). - Fixed inject_model_override doc comment accuracy (Copilot). - Added comment explaining multi-tenant notification routing priority (Copilot). Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * feat: user-scoped webhook endpoint for multi-tenant isolation Adds POST /api/webhooks/u/{user_id}/{path} — a user-scoped webhook endpoint that filters the routine lookup by user_id, preventing cross-user webhook triggering when paths collide. The existing /api/webhooks/{path} endpoint remains unchanged for backward compatibility in single-user deployments. Changes: - get_webhook_routine_by_path gains user_id: Option<&str> param - Both postgres and libsql implementations add AND user_id = ? filter when user_id is provided - New webhook_trigger_user_scoped_handler extracts (user_id, path) from URL and passes to shared fire_webhook_inner logic - Route registered on public router (webhooks are called by external services that can't send bearer tokens) Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * feat: add TenantCtx for compile-time tenant isolation Implements zmanian's architectural proposal from #1614 review: two-tier scoped database access (TenantScope/AdminScope) so handler code cannot accidentally bypass tenant scoping. TenantScope (default): wraps user_id + Arc<dyn Database>, auto-binds user_id on every operation. ID-based lookups return None for cross- tenant resources. No escape hatch — forgetting to scope is a compile error. AdminScope (explicit opt-in): cross-tenant access for system-level components (heartbeat, routine engine, self-repair, scheduler, worker). TenantCtx bundles TenantScope + workspace + cost guard + per-user rate limiting. Constructed once per request in handle_message, threaded through all command handlers and ChatDelegate. Key changes: - New src/tenant.rs (~920 lines): TenantScope, AdminScope, TenantCtx, TenantRateState, TenantRateRegistry - All command handlers: user_id: &str → ctx: &TenantCtx - ChatDelegate: cost check/record/settings via self.tenant - System components: store field changed to AdminScope - Config: TENANT_MAX_LLM_CONCURRENT, TENANT_MAX_JOBS_CONCURRENT env vars - Fixes bug: /status <job_id> cross-tenant leak (now auto-filtered) Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> --------- Co-authored-by: Claude Opus 4.6 (1M context) <[email protected]>
99 lines
5.2 KiB
Rust
99 lines
5.2 KiB
Rust
//! NEAR AI Agentic Worker Framework
|
|
//!
|
|
//! An LLM-powered autonomous agent that operates on the NEAR AI marketplace.
|
|
//!
|
|
//! # Architecture
|
|
//!
|
|
//! ```text
|
|
//! ┌─────────────────────────────────────────────────────────────────────────────────┐
|
|
//! │ User Interaction Layer │
|
|
//! │ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
|
|
//! │ │ CLI │ │ Slack │ │ Telegram │ │ HTTP │ │
|
|
//! │ └────┬─────┘ └────┬─────┘ └────┬─────┘ └────┬─────┘ │
|
|
//! │ └─────────────┴────────────┬┴─────────────┘ │
|
|
//! └──────────────────────────────────┼──────────────────────────────────────────────┘
|
|
//! ▼
|
|
//! ┌──────────────────────────────────────────────────────────────────────────────────┐
|
|
//! │ Main Agent Loop │
|
|
//! │ ┌────────────────┐ ┌────────────────┐ ┌────────────────┐ │
|
|
//! │ │ Message Router │──│ LLM Reasoning │──│ Action Executor│ │
|
|
//! │ └────────────────┘ └───────┬────────┘ └───────┬────────┘ │
|
|
//! │ ▲ │ │ │
|
|
//! │ │ ┌──────────┴───────────────────┴──────────┐ │
|
|
//! │ │ ▼ ▼ │
|
|
//! │ ┌──────┴─────────────┐ ┌───────────────────────┐ │
|
|
//! │ │ Safety Layer │ │ Self-Repair │ │
|
|
//! │ │ - Input sanitizer │ │ - Stuck job detection │ │
|
|
//! │ │ - Injection defense│ │ - Tool fixer │ │
|
|
//! │ └────────────────────┘ └───────────────────────┘ │
|
|
//! └──────────────────────────────────────────────────────────────────────────────────┘
|
|
//! ```
|
|
//!
|
|
//! # Features
|
|
//!
|
|
//! - **Multi-channel interaction** - CLI, Slack, Telegram, HTTP webhooks
|
|
//! - **Parallel job execution** - Run multiple jobs with isolated contexts
|
|
//! - **Pluggable tools** - MCP, 3rd party services, dynamic tools
|
|
//! - **Self-repair** - Detect and fix stuck jobs and broken tools
|
|
//! - **Prompt injection defense** - Sanitize all external data
|
|
//! - **Continuous learning** - Improve estimates from historical data
|
|
|
|
pub mod agent;
|
|
pub mod app;
|
|
pub mod boot_screen;
|
|
pub mod bootstrap;
|
|
pub mod channels;
|
|
pub mod cli;
|
|
pub mod config;
|
|
pub mod context;
|
|
pub mod db;
|
|
pub mod document_extraction;
|
|
pub mod error;
|
|
pub mod estimation;
|
|
pub mod evaluation;
|
|
pub mod extensions;
|
|
pub mod history;
|
|
pub mod hooks;
|
|
#[cfg(feature = "import")]
|
|
pub mod import;
|
|
pub mod llm;
|
|
pub mod observability;
|
|
pub mod orchestrator;
|
|
pub mod pairing;
|
|
pub mod profile;
|
|
pub mod registry;
|
|
pub mod safety;
|
|
pub mod sandbox;
|
|
pub mod secrets;
|
|
pub mod service;
|
|
pub mod settings;
|
|
pub mod setup;
|
|
pub mod skills;
|
|
pub mod tenant;
|
|
pub mod timezone;
|
|
pub mod tools;
|
|
pub mod tracing_fmt;
|
|
pub mod tunnel;
|
|
pub mod util;
|
|
pub mod webhooks;
|
|
pub mod worker;
|
|
pub mod workspace;
|
|
|
|
#[cfg(test)]
|
|
pub mod testing;
|
|
|
|
pub use config::Config;
|
|
pub use error::{Error, Result};
|
|
|
|
/// Re-export commonly used types.
|
|
pub mod prelude {
|
|
pub use crate::channels::{Channel, IncomingMessage, MessageStream};
|
|
pub use crate::config::Config;
|
|
pub use crate::context::{JobContext, JobState};
|
|
pub use crate::error::{Error, Result};
|
|
pub use crate::llm::LlmProvider;
|
|
pub use crate::safety::{SanitizedOutput, Sanitizer};
|
|
pub use crate::tools::{Tool, ToolOutput, ToolRegistry};
|
|
pub use crate::workspace::{MemoryDocument, Workspace};
|
|
}
|