use std::collections::BTreeSet;
use harn_serve::adapters::acp::{ACP_SESSION_UPDATE_VARIANTS, HARN_SESSION_UPDATE_EXTENSIONS};
#[cfg(test)]
use harn_vm::agent_events::WorkerEvent;
use harn_vm::agent_events::{
AgentLifecycleEvent, AgentLifecycleState, AgentTerminalKind, ToolCallErrorCategory,
ToolCallStatus, ToolMutationStatus,
};
use harn_vm::llm::AgentTerminalClass;
use harn_vm::tool_annotations::{SideEffectLevel, ToolKind};
use serde::Serialize;
use serde_json::{json, Value as JsonValue};
pub(super) fn all_acp_session_updates() -> Vec<String> {
unique_ordered(
ACP_SESSION_UPDATE_VARIANTS
.iter()
.chain(HARN_SESSION_UPDATE_EXTENSIONS.iter())
.copied(),
)
}
pub(super) fn tool_kind_values() -> Vec<String> {
ToolKind::ALL.iter().map(serde_wire_string).collect()
}
pub(super) fn tool_call_status_values() -> Vec<String> {
ToolCallStatus::ALL
.iter()
.map(|status| status.as_str().to_string())
.collect()
}
pub(super) fn tool_call_error_category_values() -> Vec<String> {
ToolCallErrorCategory::ALL
.iter()
.map(|category| category.as_str().to_string())
.collect()
}
pub(super) fn tool_mutation_status_values() -> Vec<String> {
ToolMutationStatus::ALL
.iter()
.map(|status| status.as_str().to_string())
.collect()
}
pub(super) fn agent_terminal_class_values() -> Vec<String> {
AgentTerminalClass::ALL
.iter()
.map(|class| class.as_str().to_string())
.collect()
}
pub(super) fn agent_terminal_kind_values() -> Vec<String> {
AgentTerminalKind::ALL
.iter()
.map(|kind| kind.as_str().to_string())
.collect()
}
pub(super) fn agent_terminal_owner_values() -> Vec<String> {
unique_ordered(AgentTerminalKind::ALL.iter().map(|kind| kind.owner()))
}
pub(super) fn worker_status_values() -> Vec<String> {
agent_lifecycle_state_values()
}
pub(super) fn agent_lifecycle_state_values() -> Vec<String> {
AgentLifecycleState::ALL
.iter()
.map(|state| state.wire_name().to_string())
.collect()
}
pub(super) fn agent_lifecycle_event_values() -> Vec<String> {
AgentLifecycleEvent::ALL
.iter()
.map(|event| event.as_str().to_string())
.collect()
}
pub(super) fn agent_lifecycle_state_projections() -> Vec<JsonValue> {
AgentLifecycleState::ALL
.iter()
.map(|state| {
let projection = state.projection();
json!({
"wire": projection.wire_name,
"terminal": projection.terminal,
"resumable": projection.resumable,
"runRecordStatus": projection.run_record_status,
"a2aTaskState": projection.a2a_task_state,
"aliases": state.aliases(),
})
})
.collect()
}
#[cfg(test)]
pub(super) fn assert_worker_lifecycle_parity() {
for event in WorkerEvent::ALL {
let status = event.as_status();
let state = AgentLifecycleState::from_wire(status)
.unwrap_or_else(|| panic!("worker status `{status}` missing from lifecycle registry"));
assert_eq!(state.wire_name(), status);
assert_eq!(event.is_terminal(), state.is_terminal());
assert_eq!(event.lifecycle_event().target_state(), Some(state));
}
for state in AgentLifecycleState::ALL {
assert!(
WorkerEvent::ALL
.iter()
.any(|event| event.as_status() == state.wire_name()),
"lifecycle state `{}` has no WorkerEvent projection",
state.wire_name()
);
}
}
pub(super) fn side_effect_level_values() -> Vec<String> {
SideEffectLevel::ALL
.iter()
.map(|level| level.as_str().to_string())
.collect()
}
pub(super) fn unique_ordered<'a>(values: impl Iterator<Item = &'a str>) -> Vec<String> {
let mut seen = BTreeSet::new();
let mut out = Vec::new();
for value in values {
if seen.insert(value) {
out.push(value.to_string());
}
}
out
}
pub(super) fn serde_wire_string<T: Serialize>(value: &T) -> String {
serde_json::to_value(value)
.expect("wire enum serializes")
.as_str()
.expect("wire enum serializes as string")
.to_string()
}
pub(super) fn strs_to_strings(values: &[&str]) -> Vec<String> {
values.iter().map(|value| (*value).to_string()).collect()
}