openlatch-client 0.6.15

OpenLatch runtime enforcement node — the capture-and-enforce adapter that evaluates every covered action against a coding agent's Autonomy Zone before it runs
pub mod claude_code;
pub mod cline;
pub mod codex_cli;
pub mod cursor;
pub mod opencode;

use crate::core::envelope::alias::AliasTable;
use crate::hooks::binding::EnvCommand;

/// One agent's [`crate::hooks::binding::ProxyDelivery::EnvFile`] delivery, by agent: the same
/// constants each binding declares its delivery from, so the Windows launcher shims
/// (`hooks::cli_shim`) and `env.sh` cannot disagree on a command, a guard or a default.
#[derive(Debug)]
#[cfg_attr(not(windows), allow(dead_code))] // `agent` and `defaults` are the launcher's
pub(crate) struct EnvFileDelivery {
    pub agent: &'static str,
    pub commands: &'static [EnvCommand],
    pub defaults: &'static [(&'static str, &'static str)],
}

/// Every binding's `EnvFile` delivery, in detection order.
pub(crate) const ENV_FILE_DELIVERIES: &[EnvFileDelivery] = &[
    EnvFileDelivery {
        agent: cursor::CursorBinding::AGENT_TYPE,
        commands: cursor::CURSOR_COMMANDS,
        defaults: &[],
    },
    EnvFileDelivery {
        agent: opencode::OpencodeBinding::AGENT_TYPE,
        commands: opencode::OPENCODE_COMMANDS,
        defaults: opencode::OPENCODE_DEFAULTS,
    },
    EnvFileDelivery {
        agent: cline::ClineBinding::AGENT_TYPE,
        commands: cline::CLINE_COMMANDS,
        defaults: cline::CLI_ENV_DEFAULTS,
    },
];

/// Every binding's alias table, in detection order.
fn tables() -> [&'static AliasTable; 5] {
    [
        &claude_code::ALIASES,
        &codex_cli::ALIASES,
        &cursor::ALIASES,
        &opencode::ALIASES,
        &cline::ALIASES,
    ]
}

/// The alias table of the binding an event's `source` names, or `None` for a
/// source no binding answers for — an unknown agent, or a fork, whose tools
/// are then neither mapped nor claimed.
pub fn aliases_for(source: &str) -> Option<&'static AliasTable> {
    tables().into_iter().find(|table| table.agent == source)
}

/// The `olbinding` a lane decided through: the binding's own id for the lane
/// that is not a hook (Cline's enforcement plugin) when `renders`, otherwise
/// its hook. A source no binding answers for is `<source>_hook`.
pub fn binding_id(source: &str, renders: bool) -> String {
    match aliases_for(source) {
        Some(table) => table.binding_id(renders).to_string(),
        None => format!("{}_hook", source.replace('-', "_")),
    }
}