theway-daemon 0.1.25

theway daemon — the single agent-runtime kernel (bin `thewayd`): harness assembly, local/sandbox tool policy, triggers/cron/session/DAG runtime, skills, MCP/LSP wiring, serving the gRPC/HTTP/MCP transports from theway-transport. Terminal UI lives in the theway-tui crate.
Documentation
use super::*;
use tempfile::tempdir;
use theway_contract::session::{SessionBinding, SessionRuntimeContext};

fn binding(work_dir: &std::path::Path, client_key: &str) -> SessionBinding {
    SessionBinding {
        client_key: client_key.into(),
        runtime: SessionRuntimeContext {
            work_dir: work_dir.to_string_lossy().into_owned(),
            provider: Some("provider".into()),
            model: Some("model".into()),
            base_url: None,
            thinking: None,
        },
    }
}

fn registered_work_dir() -> (tempfile::TempDir, std::path::PathBuf) {
    let dir = tempdir().unwrap();
    let work = dir.path().join("work");
    std::fs::create_dir_all(&work).unwrap();
    (dir, work)
}

fn registered_registry() -> (tempfile::TempDir, SessionExecutionRegistry) {
    let (dir, work) = registered_work_dir();
    let registry = SessionExecutionRegistry::new();
    registry
        .set("s1", binding(&work, "client-1"))
        .unwrap();
    (dir, registry)
}

#[test]
fn set_rejects_missing_work_dir() {
    let registry = SessionExecutionRegistry::new();
    let missing = std::path::Path::new("/definitely/missing/theway-work");

    let err = registry
        .set("s1", binding(missing, "client-1"))
        .unwrap_err();

    assert!(matches!(err, RegistryError::WorkDirMissing(_)));
}

#[test]
fn set_rejects_non_directory_work_dir() {
    let dir = tempdir().unwrap();
    let file = dir.path().join("file");
    std::fs::write(&file, b"not a directory").unwrap();
    let registry = SessionExecutionRegistry::new();

    let err = registry.set("s1", binding(&file, "client-1")).unwrap_err();

    assert!(matches!(err, RegistryError::WorkDirNotDirectory(_)));
}

#[test]
fn set_rejects_empty_client_key() {
    let (_dir, work) = registered_work_dir();
    let registry = SessionExecutionRegistry::new();

    let err = registry
        .set("s1", binding(&work, "   "))
        .unwrap_err();

    assert!(matches!(err, RegistryError::EmptyClientKey));
}

#[test]
fn set_allows_same_session_same_client_and_work_dir_rebind() {
    let (_dir, work) = registered_work_dir();
    let registry = SessionExecutionRegistry::new();

    registry
        .set("s1", binding(&work, "client-1"))
        .unwrap();
    registry
        .set("s1", binding(&work, "client-1"))
        .unwrap();

    assert!(registry.get("s1").is_some());
}

#[test]
fn get_returns_canonicalized_existing_work_dir() {
    let (_dir, work) = registered_work_dir();
    let registry = SessionExecutionRegistry::new();
    let non_canonical = work.join(".");

    registry
        .set("s1", binding(&non_canonical, "client-1"))
        .unwrap();

    let bound = registry.get("s1").unwrap();
    let canonical = std::fs::canonicalize(&work).unwrap();
    assert_eq!(bound.runtime.work_dir, canonical.to_string_lossy());
}

#[test]
fn set_rejects_same_session_client_key_rebind() {
    let (dir, registry) = registered_registry();
    let work = dir.path().join("work");

    let err = registry
        .set("s1", binding(&work, "client-2"))
        .unwrap_err();

    assert!(matches!(err, RegistryError::SessionClientKeyConflict { .. }));
}

#[test]
fn set_rejects_same_session_work_dir_rebind() {
    let dir = tempdir().unwrap();
    let work_a = dir.path().join("a");
    let work_b = dir.path().join("b");
    std::fs::create_dir_all(&work_a).unwrap();
    std::fs::create_dir_all(&work_b).unwrap();
    let registry = SessionExecutionRegistry::new();
    registry
        .set("s1", binding(&work_a, "client-1"))
        .unwrap();

    let err = registry
        .set("s1", binding(&work_b, "client-1"))
        .unwrap_err();

    assert!(matches!(err, RegistryError::SessionWorkDirConflict { .. }));
}

#[test]
fn set_rejects_client_key_reused_by_another_session_in_same_cwd() {
    let (_dir, work) = registered_work_dir();
    let registry = SessionExecutionRegistry::new();
    registry
        .set("s1", binding(&work, "client-1"))
        .unwrap();

    let err = registry
        .set("s2", binding(&work, "client-1"))
        .unwrap_err();

    assert!(matches!(err, RegistryError::ClientKeyCwdConflict { .. }));
}

#[test]
fn set_credential_requires_registered_session() {
    let registry = SessionExecutionRegistry::new();

    let err = registry
        .set_credential("missing", "provider", b"secret".to_vec())
        .unwrap_err();

    assert_eq!(err, RegistryError::SessionNotRegistered("missing".into()));
}

#[test]
fn set_get_credential_round_trips_provider_scoped_bytes() {
    let (_dir, registry) = registered_registry();

    registry
        .set_credential("s1", "provider-a", b"alpha".to_vec())
        .unwrap();
    registry
        .set_credential("s1", "provider-b", b"beta".to_vec())
        .unwrap();

    let alpha = registry.get_credential("s1", "provider-a").unwrap();
    let beta = registry.get_credential("s1", "provider-b").unwrap();
    assert_eq!(&*alpha.into_zeroizing(), b"alpha");
    assert_eq!(&*beta.into_zeroizing(), b"beta");
}

#[test]
fn clear_credential_removes_only_requested_provider() {
    let (_dir, registry) = registered_registry();
    registry
        .set_credential("s1", "provider-a", b"alpha".to_vec())
        .unwrap();
    registry
        .set_credential("s1", "provider-b", b"beta".to_vec())
        .unwrap();

    assert!(registry.clear_credential("s1", "provider-a"));
    assert!(!registry.clear_credential("s1", "provider-a"));
    assert!(registry.get_credential("s1", "provider-a").is_none());
    let beta = registry.get_credential("s1", "provider-b").unwrap();
    assert_eq!(&*beta.into_zeroizing(), b"beta");
}

#[test]
fn remove_session_zeroizes_credentials_and_removes_binding() {
    let (_dir, registry) = registered_registry();
    registry
        .set_credential("s1", "provider", b"secret".to_vec())
        .unwrap();

    assert!(registry.remove("s1"));
    assert!(!registry.remove("s1"));
    assert!(registry.get("s1").is_none());
    assert!(registry.get_credential("s1", "provider").is_none());
}

#[test]
fn clear_credentials_removes_all_provider_secrets() {
    let (_dir, registry) = registered_registry();
    registry
        .set_credential("s1", "provider-a", b"alpha".to_vec())
        .unwrap();
    registry
        .set_credential("s1", "provider-b", b"beta".to_vec())
        .unwrap();

    assert!(registry.clear_credentials("s1"));
    assert!(!registry.clear_credentials("s1"));
    assert!(registry.get_credential("s1", "provider-a").is_none());
    assert!(registry.get_credential("s1", "provider-b").is_none());
    assert!(registry.get("s1").is_some(), "clearing credentials keeps binding");
}

#[test]
fn credential_helpers_return_false_for_missing_sessions() {
    let registry = SessionExecutionRegistry::new();

    assert!(registry.get_credential("missing", "provider").is_none());
    assert!(!registry.clear_credential("missing", "provider"));
    assert!(!registry.clear_credentials("missing"));
}

#[test]
fn clear_all_credentials_zeroizes_every_registered_session() {
    let (_dir, registry) = registered_registry();
    registry
        .set_credential("s1", "provider-a", b"alpha".to_vec())
        .unwrap();
    registry
        .set_credential("s1", "provider-b", b"beta".to_vec())
        .unwrap();
    let (dir2, work2) = registered_work_dir();
    let _ = dir2;
    registry
        .set("s2", binding(&work2, "client-2"))
        .unwrap();
    registry
        .set_credential("s2", "provider-a", b"gamma".to_vec())
        .unwrap();

    registry.clear_all_credentials();

    assert!(registry.get_credential("s1", "provider-a").is_none());
    assert!(registry.get_credential("s1", "provider-b").is_none());
    assert!(registry.get_credential("s2", "provider-a").is_none());
    assert!(registry.get("s1").is_some());
    assert!(registry.get("s2").is_some());
}