pub mod budget;
pub mod filter;
pub mod gate;
pub mod shape;
pub mod stream;
pub mod target;
pub mod universe;
pub mod vocabulary;
#[cfg(test)]
mod tests;
use crate::errors::AppError;
use filter::FilterExpr;
use serde_json::{json, Map, Value};
use std::sync::OnceLock;
#[derive(Debug, Clone, Default)]
pub struct AgentSurface {
pub command: Option<String>,
pub mutates: bool,
pub allow_unknown_keys: bool,
pub filter_scope: Option<universe::FilterScope>,
pub use_active: bool,
pub select: Vec<String>,
pub filters: Vec<FilterExpr>,
pub sort: Option<String>,
pub dedupe_by: Option<String>,
pub max_items: usize,
pub count_only: bool,
pub streamed: bool,
pub writes_receipt: bool,
pub truncate_content: usize,
pub max_output_bytes: usize,
}
impl AgentSurface {
pub fn is_noop(&self) -> bool {
self.select.is_empty()
&& self.filters.is_empty()
&& self.sort.is_none()
&& self.dedupe_by.is_none()
&& self.max_items == 0
&& !self.count_only
&& self.truncate_content == 0
&& self.max_output_bytes == 0
}
}
static SURFACE: OnceLock<AgentSurface> = OnceLock::new();
pub fn init(surface: AgentSurface) {
let _ = SURFACE.set(surface);
}
pub fn get() -> &'static AgentSurface {
static INERT: OnceLock<AgentSurface> = OnceLock::new();
SURFACE
.get()
.unwrap_or_else(|| INERT.get_or_init(AgentSurface::default))
}
pub fn active() -> bool {
!get().is_noop()
}
pub fn apply_global(value: Value) -> Result<Value, AppError> {
apply(get(), value)
}
const META_KEY: &str = "agent_surface";
const TRUNCATED_KEY: &str = "truncated";
pub fn apply(surface: &AgentSurface, value: Value) -> Result<Value, AppError> {
let ceiling = universe::get();
apply_with_premises(surface, value, target::record(surface, ceiling), ceiling)
}
pub fn apply_with_target(
surface: &AgentSurface,
value: Value,
target: Option<Map<String, Value>>,
) -> Result<Value, AppError> {
apply_with_premises(surface, value, target, universe::get())
}
pub fn apply_with_premises(
surface: &AgentSurface,
mut value: Value,
target: Option<Map<String, Value>>,
ceiling: Option<&universe::QueryCeiling>,
) -> Result<Value, AppError> {
if is_passthrough(&value) {
return Ok(value);
}
if surface.is_noop() {
if let Some(meta) = target {
attach_meta(&mut value, &meta, false);
}
return Ok(value);
}
let array_key = locate_result_array(&value);
let aliases_removed = suppress_alias_arrays(surface, &mut value, array_key.as_deref());
let items = take_items(&mut value, array_key.as_deref());
const NO_ELEMENTS: &[Value] = &[];
let findings = gate::evaluate(
surface,
&vocabulary::Scope::new(items.as_deref().unwrap_or(NO_ELEMENTS), &value)
.with_command(surface.command.as_deref()),
array_key.as_deref(),
items.is_some(),
ceiling,
)?;
let (payload, mut meta) = match items {
Some(items) => shape_items(surface, value, array_key.as_deref(), items, ceiling),
None => shape_scalar_envelope(surface, value, ceiling),
};
if let Some(record) = target {
meta.extend(record);
}
if !aliases_removed.is_empty() {
meta.insert("aliases_removed".into(), json!(aliases_removed));
}
if findings.is_partial() {
meta.insert("unresolved_keys".into(), json!(findings.unresolved_keys));
meta.insert("resolved_keys".into(), json!(findings.resolved_keys));
meta.insert("key_resolution".into(), json!("partial"));
if !findings.key_suggestions.is_empty() {
meta.insert("key_suggestions".into(), json!(findings.key_suggestions));
}
if findings.vocabulary_partial {
meta.insert("vocabulary_partial".into(), Value::Bool(true));
}
}
if let Some(key) = array_key.as_deref() {
let source = if is_declared_result_array(key) {
ARRAY_SOURCE_DECLARED
} else {
ARRAY_SOURCE_FALLBACK
};
meta.insert("result_array_source".into(), json!(source));
}
Ok(finalize(surface, payload, array_key.as_deref(), meta))
}
fn suppress_alias_arrays(
surface: &AgentSurface,
value: &mut Value,
array_key: Option<&str>,
) -> Vec<String> {
let Some(canonical) = array_key else {
return Vec::new();
};
let Some(command) = surface.command.as_deref() else {
return Vec::new();
};
let Some((_, _, aliases)) = crate::constants::AGENT_SURFACE_ALIAS_ARRAYS
.iter()
.find(|(cmd, key, _)| *cmd == command && *key == canonical)
else {
return Vec::new();
};
let Some(map) = value.as_object_mut() else {
return Vec::new();
};
let mut removed = Vec::new();
for alias in *aliases {
if map.get(*alias).is_some_and(Value::is_array) {
map.remove(*alias);
removed.push((*alias).to_string());
}
}
removed
}
fn is_passthrough(value: &Value) -> bool {
let Some(map) = value.as_object() else {
return false;
};
if map.contains_key("$schema") {
return true;
}
if map.get("error") == Some(&Value::Bool(true)) {
return true;
}
map.get("ok") == Some(&Value::Bool(false))
}
fn locate_result_array(value: &Value) -> Option<String> {
let map = value.as_object()?;
for candidate in crate::constants::AGENT_SURFACE_RESULT_KEYS {
if map.get(*candidate).is_some_and(Value::is_array) {
return Some((*candidate).to_string());
}
}
map.iter()
.find(|(_, v)| v.is_array())
.map(|(k, _)| k.clone())
}
fn is_declared_result_array(key: &str) -> bool {
crate::constants::AGENT_SURFACE_RESULT_KEYS.contains(&key)
}
const ARRAY_SOURCE_DECLARED: &str = "declared";
const ARRAY_SOURCE_FALLBACK: &str = "fallback";
fn take_items(value: &mut Value, array_key: Option<&str>) -> Option<Vec<Value>> {
match array_key {
Some(key) => match value.as_object_mut()?.get_mut(key)? {
Value::Array(items) => Some(std::mem::take(items)),
_ => None,
},
None => match value {
Value::Array(items) => Some(std::mem::take(items)),
_ => None,
},
}
}
fn shape_items(
surface: &AgentSurface,
mut envelope: Value,
array_key: Option<&str>,
items: Vec<Value>,
ceiling: Option<&universe::QueryCeiling>,
) -> (Value, Map<String, Value>) {
let input_count = items.len();
let command = surface.command.as_deref();
let mut items = shape::filter(items, &surface.filters, command);
if let Some(key) = &surface.sort {
items = shape::sort(items, key, command);
}
if let Some(key) = &surface.dedupe_by {
items = shape::dedupe(items, key, command);
}
let matched_count = items.len();
items = shape::limit(items, surface.max_items);
items = shape::project(items, &surface.select, command);
let output_count = items.len();
if surface.count_only && !surface.writes_receipt {
let mut meta = base_meta(surface, input_count, output_count, ceiling);
meta.insert("count_only".into(), Value::Bool(true));
meta.insert(
"count_scope".into(),
json!(universe::count_scope(output_count, matched_count, ceiling)),
);
return (json!({ "count": output_count }), meta);
}
let secondary_capped = cap_secondary_arrays(&mut envelope, surface.max_items);
match array_key {
Some(key) => {
if let Some(map) = envelope.as_object_mut() {
map.insert(key.to_string(), Value::Array(items));
}
}
None => envelope = Value::Array(items),
}
let mut meta = base_meta(surface, input_count, output_count, ceiling);
if surface.count_only {
meta.insert("count_only_suppressed".into(), Value::Bool(true));
}
if !secondary_capped.is_empty() {
meta.insert("secondary_capped".into(), json!(secondary_capped));
}
(envelope, meta)
}
fn cap_secondary_arrays(envelope: &mut Value, max_items: usize) -> Vec<String> {
if max_items == 0 {
return Vec::new();
}
let Some(map) = envelope.as_object_mut() else {
return Vec::new();
};
let mut capped = Vec::new();
for (key, value) in map.iter_mut() {
if let Value::Array(items) = value {
if items.len() > max_items {
items.truncate(max_items);
capped.push(key.clone());
}
}
}
capped
}
fn shape_scalar_envelope(
surface: &AgentSurface,
envelope: Value,
ceiling: Option<&universe::QueryCeiling>,
) -> (Value, Map<String, Value>) {
if surface.count_only && !surface.writes_receipt {
let mut meta = base_meta(surface, 1, 1, ceiling);
meta.insert("count_only".into(), Value::Bool(true));
meta.insert("count_scope".into(), json!(universe::COUNT_SCOPE_SCALAR));
return (json!({ "count": 1 }), meta);
}
let projected = shape::project_one(envelope, &surface.select, surface.command.as_deref());
let mut meta = base_meta(surface, 1, 1, ceiling);
if surface.count_only {
meta.insert("count_only_suppressed".into(), Value::Bool(true));
}
(projected, meta)
}
fn base_meta(
surface: &AgentSurface,
input: usize,
output: usize,
ceiling: Option<&universe::QueryCeiling>,
) -> Map<String, Value> {
let mut meta = Map::new();
meta.insert("input_count".into(), json!(input));
meta.insert("output_count".into(), json!(output));
if !surface.select.is_empty() {
meta.insert("select".into(), json!(surface.select));
}
if !surface.filters.is_empty() {
meta.insert("filters".into(), json!(surface.filters.len()));
}
if let Some(key) = &surface.sort {
meta.insert("sort".into(), json!(key));
}
if let Some(key) = &surface.dedupe_by {
meta.insert("dedupe_by".into(), json!(key));
}
if surface.max_items > 0 {
meta.insert("max_items".into(), json!(surface.max_items));
}
universe::insert_query_ceiling(&mut meta, ceiling);
meta
}
fn finalize(
surface: &AgentSurface,
mut payload: Value,
array_key: Option<&str>,
mut meta: Map<String, Value>,
) -> Value {
let content_truncated = shape::truncate_strings(&mut payload, surface.truncate_content);
if content_truncated {
meta.insert("content_truncated".into(), Value::Bool(true));
meta.insert("truncate_content".into(), json!(surface.truncate_content));
}
attach_meta(&mut payload, &meta, content_truncated);
let headroom = budget_headroom(surface, &payload, &meta);
let effective_max = match surface.max_output_bytes {
0 => 0,
max => max.saturating_sub(headroom).max(1),
};
let outcome = budget::enforce(&mut payload, array_key, effective_max);
if outcome.truncated && !outcome.stub {
meta.insert("output_truncated".into(), Value::Bool(true));
meta.insert("dropped".into(), json!(outcome.dropped));
meta.insert("max_output_bytes".into(), json!(surface.max_output_bytes));
if let Some(surviving) = surviving_len(&payload, array_key) {
meta.insert("output_count".into(), json!(surviving));
}
attach_meta(&mut payload, &meta, true);
}
if outcome.stub {
if let Some(map) = payload.as_object_mut() {
map.insert("max_output_bytes".into(), json!(surface.max_output_bytes));
}
}
payload
}
fn surviving_len(payload: &Value, array_key: Option<&str>) -> Option<usize> {
match array_key {
Some(key) => payload.get(key)?.as_array().map(Vec::len),
None => payload.as_array().map(Vec::len),
}
}
fn budget_headroom(surface: &AgentSurface, payload: &Value, meta: &Map<String, Value>) -> usize {
if surface.max_output_bytes == 0 {
return 0;
}
let widest_dropped = meta
.get("input_count")
.and_then(Value::as_u64)
.unwrap_or_default();
let mut annotated = meta.clone();
annotated.insert("output_truncated".into(), Value::Bool(true));
annotated.insert("dropped".into(), json!(widest_dropped));
annotated.insert("max_output_bytes".into(), json!(surface.max_output_bytes));
let before = encoded_len(&Value::Object(meta.clone()));
let after = encoded_len(&Value::Object(annotated));
let mut extra = after.saturating_sub(before);
if payload
.as_object()
.is_some_and(|map| !map.contains_key(TRUNCATED_KEY))
{
extra += TRUNCATED_KEY.len() + r#","":true"#.len();
}
extra
}
fn encoded_len(value: &Value) -> usize {
serde_json::to_string(value).map_or(0, |s| s.len())
}
fn attach_meta(payload: &mut Value, meta: &Map<String, Value>, truncated: bool) {
let Some(map) = payload.as_object_mut() else {
return;
};
map.insert(META_KEY.to_string(), Value::Object(meta.clone()));
if truncated {
map.insert(TRUNCATED_KEY.to_string(), Value::Bool(true));
}
}