use serde_json::Value;
pub fn prompt_of(data: &Value) -> Option<&str> {
if let Some(p) = data.get("prompt").and_then(Value::as_str) {
return non_empty(p);
}
data.as_object()?
.values()
.find_map(|v| v.get("prompt").and_then(Value::as_str))
.and_then(non_empty)
}
pub fn model_of(data: &Value) -> (Option<&str>, Option<&str>) {
let Some(model) = data.get("model") else {
return (None, None);
};
if let Some(slug) = model.as_str() {
return (None, non_empty(slug));
}
(
model
.get("provider")
.and_then(Value::as_str)
.and_then(non_empty),
model
.get("slug")
.and_then(Value::as_str)
.and_then(non_empty),
)
}
fn non_empty(s: &str) -> Option<&str> {
let t = s.trim();
(!t.is_empty()).then_some(s)
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json::json;
#[test]
fn the_prompt_is_found_by_shape_across_agents() {
assert_eq!(
prompt_of(&json!({"prompt": "hello", "session_id": "abc"})),
Some("hello")
);
assert_eq!(
prompt_of(&json!({
"hookName": "UserPromptSubmit",
"userPromptSubmit": {"prompt": "hey there !!"}
})),
Some("hey there !!")
);
assert_eq!(
prompt_of(&json!({"someFutureHook": {"prompt": "p"}})),
Some("p")
);
assert_eq!(prompt_of(&json!({"toolName": "Bash"})), None);
assert_eq!(prompt_of(&json!({"prompt": " "})), None);
}
#[test]
fn the_model_is_read_without_inventing_the_missing_half() {
assert_eq!(
model_of(&json!({"model": {"provider": "ollama", "slug": "qwen2.5-coder:7b"}})),
(Some("ollama"), Some("qwen2.5-coder:7b"))
);
assert_eq!(
model_of(&json!({"model": "claude-opus-5"})),
(None, Some("claude-opus-5"))
);
assert_eq!(
model_of(&json!({"model": {"provider": "unknown", "slug": "unknown"}})),
(Some("unknown"), Some("unknown"))
);
assert_eq!(model_of(&json!({})), (None, None));
}
}