feat(grid-agent): enforce central action policy (#120)
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
//! Narrow injected boundaries between orchestration and grid/world I/O.
|
||||
|
||||
use crate::types::{GridEvent, GridEventKind, PolicyDecision, ProposedToolCall, ToolCallOutcome};
|
||||
use crate::policy::AuthorizedAction;
|
||||
use crate::types::{GridEvent, GridEventKind, ToolCallOutcome};
|
||||
use libremetaverse_types::compat::CancellationToken;
|
||||
use std::error::Error;
|
||||
use std::fmt;
|
||||
@@ -49,18 +50,21 @@ pub trait GridBackend: Send + Sync + 'static {
|
||||
) -> BackendFuture<'_, Result<(), BackendError>>;
|
||||
}
|
||||
|
||||
/// The sole world-mutation boundary. Later tool implementations cannot bypass
|
||||
/// the policy decision passed to this trait, and fake/live implementations use
|
||||
/// the same call shape.
|
||||
pub trait WorldMutator: Send + Sync + 'static {
|
||||
/// Backend for an already authorized tool action. `AuthorizedAction` has no
|
||||
/// public constructor and binds the exact name, arguments, principal, budget,
|
||||
/// and one policy authorization.
|
||||
pub trait AuthorizedToolBackend: Send + Sync + 'static {
|
||||
fn apply(
|
||||
&self,
|
||||
call: ProposedToolCall,
|
||||
decision: PolicyDecision,
|
||||
action: AuthorizedAction,
|
||||
cancellation: CancellationToken,
|
||||
) -> BackendFuture<'_, Result<ToolCallOutcome, BackendError>>;
|
||||
}
|
||||
|
||||
/// Marker for world-mutating backends. Production mutations use the same
|
||||
/// opaque authorization boundary as every other tool backend.
|
||||
pub trait WorldMutator: AuthorizedToolBackend {}
|
||||
|
||||
/// Inert deterministic backend used by the foundational offline service.
|
||||
///
|
||||
/// It performs no login or network operation and is available without the
|
||||
|
||||
@@ -7,13 +7,20 @@
|
||||
pub mod backend;
|
||||
pub mod config;
|
||||
pub mod llm;
|
||||
pub mod policy;
|
||||
pub mod service;
|
||||
pub mod tool_loop;
|
||||
pub mod types;
|
||||
|
||||
#[cfg(test)]
|
||||
mod policy_tests;
|
||||
|
||||
#[cfg(feature = "live-grid")]
|
||||
pub use backend::LibremetaverseClientOwner;
|
||||
pub use backend::{BackendError, BackendFuture, GridBackend, OfflineGridBackend, WorldMutator};
|
||||
pub use backend::{
|
||||
AuthorizedToolBackend, BackendError, BackendFuture, GridBackend, OfflineGridBackend,
|
||||
WorldMutator,
|
||||
};
|
||||
pub use config::{
|
||||
AgentConfig, BehaviorSettings, ConfigError, ConfigLoader, EndpointUrl, Environment,
|
||||
GridConnection, Limits, LlmConnection, MapEnvironment, OperatingMode, SecretString,
|
||||
@@ -23,6 +30,14 @@ pub use llm::{
|
||||
Completion, CompletionMessage, ContentPart, ImageDetail, LlmClient, LlmError,
|
||||
LlmTransportLimits, ToolDefinition, ToolSchema, Usage,
|
||||
};
|
||||
pub use policy::{
|
||||
ActionOrigin, AllowedOrigins, ApprovalId, ApprovalRule, AuthenticatedPrincipal,
|
||||
AuthorizedAction, BudgetLimits, Capability, FixedCost, Idempotency, MemoryPolicyAudit,
|
||||
OriginClass, PolicyAuditError, PolicyAuditRecord, PolicyAuditSink, PolicyDisposition,
|
||||
PolicyError, PolicyEvaluation, PolicyFinalOutcome, PolicyGateway, PolicyLimits,
|
||||
PolicyReasonCode, PolicyRequestContext, PolicySnapshot, PolicyTool, PolicyToolExecutor,
|
||||
ResourceCost, ResourceEstimator, Risk, SchedulerGrantId, UntrustedData, UntrustedSource,
|
||||
};
|
||||
pub use service::{AgentService, ServiceError, ServiceHandle, ServiceState};
|
||||
pub use tool_loop::{
|
||||
HistorySummarizer, SessionGeneration, ToolExecution, ToolExecutor, ToolFuture, ToolLoop,
|
||||
@@ -30,6 +45,6 @@ pub use tool_loop::{
|
||||
};
|
||||
pub use types::{
|
||||
BoundaryError, BoundedText, BoundedVec, ControlCommand, Conversation, ConversationMessage,
|
||||
GridEvent, GridEventKind, LlmRequest, LlmResult, MessageRole, ObservableEvent, PolicyDecision,
|
||||
GridEvent, GridEventKind, LlmRequest, LlmResult, MessageRole, ObservableEvent,
|
||||
ProposedToolCall, ToolCallOutcome,
|
||||
};
|
||||
|
||||
1859
crates/metacrate-grid-agent/src/policy.rs
Normal file
1859
crates/metacrate-grid-agent/src/policy.rs
Normal file
File diff suppressed because it is too large
Load Diff
1063
crates/metacrate-grid-agent/src/policy_tests.rs
Normal file
1063
crates/metacrate-grid-agent/src/policy_tests.rs
Normal file
File diff suppressed because it is too large
Load Diff
@@ -21,8 +21,9 @@ const MAX_SESSION_TOOL_CALLS: usize = 256;
|
||||
|
||||
pub type ToolFuture<'a> = Pin<Box<dyn Future<Output = ToolExecution> + Send + 'a>>;
|
||||
|
||||
/// Downstream execution boundary. Issue #120 can implement policy evaluation
|
||||
/// here; this loop guarantees its input already passed name/schema checks.
|
||||
/// Downstream execution boundary. Production wiring uses
|
||||
/// [`crate::policy::PolicyToolExecutor`]; this loop guarantees its input already
|
||||
/// passed name/schema checks, and the policy gateway validates it again.
|
||||
pub trait ToolExecutor: Send + Sync {
|
||||
fn execute<'a>(
|
||||
&'a self,
|
||||
|
||||
@@ -347,19 +347,6 @@ impl ProposedToolCall {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub enum PolicyDecision {
|
||||
Approved {
|
||||
authorization_id: u64,
|
||||
},
|
||||
Denied {
|
||||
reason: BoundedText<MAX_OBSERVABLE_DETAIL_BYTES>,
|
||||
},
|
||||
NeedsOperatorApproval {
|
||||
prompt: BoundedText<MAX_OBSERVABLE_DETAIL_BYTES>,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub struct LlmResult {
|
||||
pub request_id: u64,
|
||||
@@ -393,10 +380,7 @@ pub enum ObservableEvent {
|
||||
state: &'static str,
|
||||
},
|
||||
Grid(GridEvent),
|
||||
Policy {
|
||||
call_id: BoundedText<MAX_IDENTIFIER_BYTES>,
|
||||
decision: PolicyDecision,
|
||||
},
|
||||
Policy(crate::policy::PolicyAuditRecord),
|
||||
Tool(ToolCallOutcome),
|
||||
Diagnostic {
|
||||
detail: BoundedText<MAX_OBSERVABLE_DETAIL_BYTES>,
|
||||
|
||||
Reference in New Issue
Block a user