leviath-cli 0.1.0

Command-line interface for Leviath agent framework
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
//! The agent blueprints shipped inside the `lev` binary, and the planner that
//! decides what to do with them.
//!
//! Embedding is what makes the ten blueprints under the workspace's `agents/`
//! directory reachable outside a git checkout: `lev add` takes a local path,
//! and an `agents/` directory next to the executable is a layout no real
//! install has, so without the bundle a user who downloads a release binary
//! gets a working runtime and zero agents to run on it.
//!
//! `build.rs` embeds every file of every blueprint via `include_str!` (23
//! files, ~170 KB of text) and generates the [`BUNDLED_AGENTS`] table included
//! below. `lev setup` offers to install them; `lev list` reports them.

include!(concat!(env!("OUT_DIR"), "/bundled_agents.rs"));

use std::path::Path;

/// What `lev setup` should do with one bundled blueprint, given what is
/// currently installed.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum AgentAction {
    /// Not installed.
    Install,
    /// Installed at a different version.
    Update { from: String },
    /// Installed at the bundled version.
    UpToDate,
}

impl AgentAction {
    /// Whether applying this action would change anything on disk. Drives which
    /// rows the wizard pre-checks.
    pub fn is_change(&self) -> bool {
        !matches!(self, Self::UpToDate)
    }

    /// Short label for the wizard's blueprint list.
    pub fn label(&self, to: &str) -> String {
        match self {
            Self::Install => format!("install {to}"),
            Self::Update { from } => format!("update {from}{to}"),
            Self::UpToDate => "up to date".to_string(),
        }
    }
}

/// The installed version of `name` under `agents_dir`, if a readable manifest
/// is there.
///
/// Deliberately lenient: a blueprint directory whose manifest is missing or
/// unparseable reads as *not installed*, so the wizard offers a clean reinstall
/// instead of refusing to plan. An unreadable manifest is exactly the state a
/// half-finished copy leaves behind.
pub fn installed_version(agents_dir: &Path, name: &str) -> Option<String> {
    let manifest = std::fs::read_to_string(agents_dir.join(name).join("agent.leviath")).ok()?;
    leviath_core::manifest::parse_manifest(&manifest)
        .ok()
        .map(|bp| bp.version)
}

/// Decide what to do with every bundled blueprint.
///
/// Version comparison is plain string inequality, not semver ordering: this
/// crate has no semver dependency, and both versions are shown to the user
/// anyway, so a hand-edited blueprint surfaces as an offered update they can
/// decline rather than being silently overwritten or silently skipped.
///
/// Known limitation: a blueprint edited *without* bumping its version reads as
/// up to date, because nothing hashes the contents.
pub fn plan_agent_actions(agents_dir: &Path) -> Vec<(&'static BundledAgent, AgentAction)> {
    BUNDLED_AGENTS
        .iter()
        .map(|agent| {
            let action = match installed_version(agents_dir, agent.name) {
                None => AgentAction::Install,
                Some(v) if v == agent.version => AgentAction::UpToDate,
                Some(from) => AgentAction::Update { from },
            };
            (agent, action)
        })
        .collect()
}

/// Write one bundled blueprint into `<agents_dir>/<name>/`, replacing whatever
/// is there.
///
/// The existing tree is removed first rather than merged over: a stale file
/// from an older version of the blueprint (a tool script that was dropped, say)
/// would otherwise survive forever and keep being loaded. This mirrors what
/// `lev add`'s directory install already does.
pub fn install_bundled(agent: &BundledAgent, agents_dir: &Path) -> anyhow::Result<()> {
    let dest = agents_dir.join(agent.name);
    if dest.exists() {
        std::fs::remove_dir_all(&dest)?;
    }
    for (rel, contents) in agent.files {
        // Derive the parent from the *relative* path rather than calling
        // `path.parent()`. `dest.join(rel)` always has a parent, so the `None`
        // arm of `parent()` would be unreachable code pretending to be a
        // handled case; splitting `rel` gives two arms that both actually
        // happen - nested (`tools/web_fetch.rhai`) and flat (`agent.leviath`).
        let parent = match rel.rsplit_once('/') {
            Some((dir, _)) => dest.join(dir),
            None => dest.clone(),
        };
        std::fs::create_dir_all(&parent)?;
        std::fs::write(dest.join(rel), contents)?;
    }
    Ok(())
}

#[cfg(test)]
mod tests {
    use super::*;

    /// Every assertion here is an invariant over *all* discovered blueprints.
    /// Naming individual agents would turn adding or renaming one into a test
    /// edit, and would stop testing the property the moment the list drifted.
    #[test]
    fn every_bundled_agent_has_a_name_version_and_manifest() {
        assert!(
            !BUNDLED_AGENTS.is_empty(),
            "the binary shipped with no blueprints -- build.rs found no agents/ directory"
        );
        for agent in BUNDLED_AGENTS {
            assert!(!agent.name.is_empty(), "a bundled agent has an empty name");
            assert!(
                !agent.version.is_empty(),
                "bundled agent {} has an empty version",
                agent.name
            );
            assert!(
                agent.files.iter().any(|(rel, _)| *rel == "agent.leviath"),
                "bundled agent {} has no agent.leviath",
                agent.name
            );
            for (rel, contents) in agent.files {
                assert!(
                    !rel.is_empty(),
                    "bundled agent {} has an empty path",
                    agent.name
                );
                assert!(
                    !contents.is_empty(),
                    "bundled agent {} has an empty file {rel}",
                    agent.name
                );
            }
        }
    }

    /// A tool script shipped under the same filename by more than one agent
    /// must be byte-identical everywhere.
    ///
    /// Each agent directory is self-contained - that is what lets `lev add
    /// <dir>` and `lev pack` work - so `web_fetch.rhai` and `web_search.rhai`
    /// exist as five copies each rather than one shared file. That is fine
    /// until one copy is fixed and the others are not: these scripts are the
    /// agents' network surface, so a hardening change applied to one of five is
    /// four agents still carrying the unfixed behaviour, with nothing to say so.
    ///
    /// This turns that silent drift into a test failure. Deliberately keyed on
    /// filename over *all* discovered agents rather than naming the five, so it
    /// keeps holding as agents are added or renamed.
    #[test]
    fn a_tool_script_shared_by_several_agents_is_identical_in_all_of_them() {
        use std::collections::HashMap;

        // filename -> (first agent that shipped it, its contents)
        let mut first_seen: HashMap<&str, (&str, &str)> = HashMap::new();
        for agent in BUNDLED_AGENTS {
            for (rel, contents) in agent.files {
                let Some(filename) = rel.strip_prefix("tools/") else {
                    continue;
                };
                match first_seen.get(filename) {
                    Some((other, expected)) => assert!(
                        expected == contents,
                        "tools/{filename} differs between bundled agents {other} and {} - \
                         a change to one copy was not applied to the others",
                        agent.name
                    ),
                    None => {
                        first_seen.insert(filename, (agent.name, contents));
                    }
                }
            }
        }
        // Guard against a vacuous pass: if the scan found no tool scripts at
        // all, the loop above asserts nothing.
        assert!(
            !first_seen.is_empty(),
            "no bundled agent ships a tools/ script - this invariant is not being tested"
        );
    }

    #[test]
    fn every_bundled_manifest_parses_and_agrees_with_its_recorded_version() {
        // The recorded version drives install/update planning, so a build.rs
        // scan that disagreed with the manifest would make the wizard lie.
        for agent in BUNDLED_AGENTS {
            let manifest = agent
                .files
                .iter()
                .find(|(rel, _)| *rel == "agent.leviath")
                .map(|(_, c)| *c)
                .expect("checked above");
            // `.expect`, not `.unwrap_or_else(|e| panic!(...))`: the closure in
            // the latter is a function that never runs on a passing test, which
            // reads to llvm-cov as an uncovered region. For the same reason the
            // message is a literal - a *call* in an `assert!`'s format args is
            // also a region that only the failing path reaches.
            let parsed = leviath_core::manifest::parse_manifest(manifest);
            assert!(
                parsed.is_ok(),
                "bundled agent {} does not parse",
                agent.name
            );
            let blueprint = parsed.expect("asserted Ok just above");
            assert_eq!(blueprint.version, agent.version);
            assert_eq!(blueprint.name, agent.name);
        }
    }

    /// Every name in a stage's `available_tools` has to resolve to a tool that
    /// exists, and every `[stages.X.tool_permissions]` key has to be a tool that
    /// stage actually grants.
    ///
    /// A typo here is invisible on its own: `filter_tools_by_available` silently
    /// omits a name matching nothing, so the stage just quietly advertises one
    /// tool fewer. And because dispatch refuses anything a stage did not offer,
    /// the same typo means the model is told the tool does not exist and the
    /// stage cannot do its job - a silent omission that is really a silent
    /// failure, which is worth a test. A permission entry for an ungranted tool is the same drift seen
    /// from the other side: it reads as a grant and is not one.
    ///
    /// An invariant over all discovered agents rather than a list of names -
    /// naming them would stop testing the property the moment the list drifted.
    #[test]
    fn every_stage_tool_name_resolves_and_every_permission_names_a_granted_tool() {
        // Sub-agent tools are provided by the host, not `BuiltinTools`.
        const SUBAGENT: &[&str] = &[
            "spawn_agent",
            "check_agent",
            "wait_for_agent",
            "send_to_agent",
            "kill_agent",
        ];
        let builtin = leviath_tools::BuiltinTools::new(leviath_tools::ToolContext::new(
            std::path::PathBuf::from("."),
        ))
        .names();

        for agent in BUNDLED_AGENTS {
            // This agent's own Rhai tools: `tools/<name>.rhai` defines `<name>`.
            let scripts: Vec<&str> = agent
                .files
                .iter()
                .filter_map(|(rel, _)| rel.strip_prefix("tools/"))
                .filter_map(|f| f.strip_suffix(".rhai"))
                .collect();
            let manifest = agent
                .files
                .iter()
                .find(|(rel, _)| *rel == "agent.leviath")
                .map(|(_, c)| *c)
                .expect("every bundled agent has a manifest");
            let parsed = leviath_core::manifest::parse_manifest(manifest);
            assert!(
                parsed.is_ok(),
                "bundled agent {} does not parse",
                agent.name
            );
            let blueprint = parsed.expect("asserted Ok just above");

            for stage in &blueprint.stages {
                for tool in &stage.available_tools {
                    // `server__tool` is an MCP tool, resolvable only once that
                    // server is installed - not something a manifest can be
                    // checked against here.
                    let known = tool.contains("__")
                        || builtin.iter().any(|b| b == tool)
                        || SUBAGENT.contains(&tool.as_str())
                        || scripts.contains(&tool.as_str());
                    assert!(
                        known,
                        "{}: stage '{}' grants '{}', which is not a built-in,                          a sub-agent tool, or one of this agent's own tools/*.rhai",
                        agent.name, stage.name, tool
                    );
                }
                for granted in stage.tool_permissions.keys() {
                    assert!(
                        stage.available_tools.contains(granted),
                        "{}: stage '{}' sets a permission for '{}', which it does                          not grant in available_tools",
                        agent.name,
                        stage.name,
                        granted
                    );
                }
            }
        }
    }

    /// The invariant above can actually fail - a check over shipped data that
    /// happens to pass says nothing about whether it would catch drift.
    #[test]
    fn the_stage_tool_invariant_rejects_a_typo_and_an_orphan_permission() {
        let builtin = leviath_tools::BuiltinTools::new(leviath_tools::ToolContext::new(
            std::path::PathBuf::from("."),
        ))
        .names();
        assert!(
            !builtin.iter().any(|b| b == "raed_file"),
            "a misspelled tool must not resolve"
        );

        let manifest = r#"
[agent]
name = "x"
version = "0.1.0"
description = "x"

[stages.only]
model = { provider = "anthropic", model = "m" }
available_tools = ["read_file"]

[stages.only.tool_permissions]
write_file = "allow"
"#;
        let bp = leviath_core::manifest::parse_manifest(manifest)
            .expect("the fixture parses; it is the invariant that should object");
        let stage = &bp.stages[0];
        assert!(
            !stage.available_tools.contains(&"write_file".to_string()),
            "the orphan-permission arm has something to catch"
        );
    }

    #[test]
    fn bundled_agent_names_are_unique() {
        let mut names: Vec<&str> = BUNDLED_AGENTS.iter().map(|a| a.name).collect();
        names.sort_unstable();
        let count = names.len();
        names.dedup();
        assert_eq!(count, names.len(), "duplicate bundled agent names");
    }

    // ─── installed_version ──────────────────────────────────────────────────

    #[test]
    fn installed_version_reads_a_manifest() {
        let dir = tempfile::tempdir().unwrap();
        let agent = &BUNDLED_AGENTS[0];
        install_bundled(agent, dir.path()).unwrap();

        assert_eq!(
            installed_version(dir.path(), agent.name).as_deref(),
            Some(agent.version)
        );
    }

    #[test]
    fn installed_version_is_none_when_nothing_is_installed() {
        let dir = tempfile::tempdir().unwrap();
        assert!(installed_version(dir.path(), "not-installed").is_none());
    }

    #[test]
    fn installed_version_is_none_for_an_unparseable_manifest() {
        // A half-written install must read as "not installed" so the wizard
        // offers a clean reinstall rather than refusing to plan.
        let dir = tempfile::tempdir().unwrap();
        std::fs::create_dir_all(dir.path().join("broken")).unwrap();
        std::fs::write(
            dir.path().join("broken/agent.leviath"),
            "not valid toml {{{",
        )
        .unwrap();

        assert!(installed_version(dir.path(), "broken").is_none());
    }

    // ─── plan_agent_actions ─────────────────────────────────────────────────

    #[test]
    fn plan_offers_to_install_everything_into_an_empty_dir() {
        let dir = tempfile::tempdir().unwrap();

        let plan = plan_agent_actions(dir.path());

        assert_eq!(plan.len(), BUNDLED_AGENTS.len());
        for (agent, action) in &plan {
            assert_eq!(*action, AgentAction::Install);
            assert!(action.is_change());
            assert_eq!(
                action.label(agent.version),
                format!("install {}", agent.version)
            );
        }
    }

    #[test]
    fn plan_reports_up_to_date_after_installing() {
        let dir = tempfile::tempdir().unwrap();
        for agent in BUNDLED_AGENTS {
            install_bundled(agent, dir.path()).unwrap();
        }

        let plan = plan_agent_actions(dir.path());

        for (agent, action) in &plan {
            assert_eq!(*action, AgentAction::UpToDate, "{}", agent.name);
            assert!(!action.is_change());
            assert_eq!(action.label(agent.version), "up to date");
        }
    }

    #[test]
    fn plan_reports_an_update_when_the_installed_version_differs() {
        let dir = tempfile::tempdir().unwrap();
        let agent = &BUNDLED_AGENTS[0];
        install_bundled(agent, dir.path()).unwrap();
        // Rewrite the installed manifest at a different version.
        let manifest_path = dir.path().join(agent.name).join("agent.leviath");
        let manifest = std::fs::read_to_string(&manifest_path).unwrap();
        let bumped = manifest.replacen(
            &format!("version = \"{}\"", agent.version),
            "version = \"9.9.9\"",
            1,
        );
        std::fs::write(&manifest_path, bumped).unwrap();

        let plan = plan_agent_actions(dir.path());
        let (_, action) = plan
            .iter()
            .find(|(a, _)| a.name == agent.name)
            .expect("the bundled agent is in the plan");

        assert_eq!(
            *action,
            AgentAction::Update {
                from: "9.9.9".to_string()
            }
        );
        assert!(action.is_change());
        assert_eq!(
            action.label(agent.version),
            format!("update 9.9.9 → {}", agent.version)
        );
    }

    // ─── install_bundled ────────────────────────────────────────────────────

    #[test]
    fn install_writes_every_file_including_nested_ones() {
        let dir = tempfile::tempdir().unwrap();
        // Pick a blueprint that actually has a nested `tools/` file, so the
        // create_dir_all arm is exercised by a real shipped layout rather than
        // a fixture. If none ships nested files any more, the flat arm below
        // still covers the rest.
        for agent in BUNDLED_AGENTS {
            install_bundled(agent, dir.path()).unwrap();
            for (rel, contents) in agent.files {
                let written = std::fs::read_to_string(dir.path().join(agent.name).join(rel));
                assert!(written.is_ok(), "{}/{rel} was not written", agent.name);
                assert_eq!(written.expect("asserted Ok just above"), *contents);
            }
        }
        assert!(
            BUNDLED_AGENTS
                .iter()
                .any(|a| a.files.iter().any(|(rel, _)| rel.contains('/'))),
            "no bundled blueprint has a nested file, so install's mkdir path is untested"
        );
    }

    #[test]
    fn install_replaces_an_existing_tree_and_drops_stale_files() {
        let dir = tempfile::tempdir().unwrap();
        let agent = &BUNDLED_AGENTS[0];
        install_bundled(agent, dir.path()).unwrap();
        let stale = dir
            .path()
            .join(agent.name)
            .join("stale-from-an-older-version");
        std::fs::write(&stale, "leftover").unwrap();

        install_bundled(agent, dir.path()).unwrap();

        assert!(
            !stale.exists(),
            "a reinstall must not leave files from the previous version behind"
        );
        assert!(dir.path().join(agent.name).join("agent.leviath").exists());
    }

    #[test]
    fn install_surfaces_a_directory_creation_failure() {
        // `agents_dir` is itself a file, so creating the blueprint directory
        // under it fails.
        let dir = tempfile::tempdir().unwrap();
        let blocked = dir.path().join("not-a-dir");
        std::fs::write(&blocked, "").unwrap();

        let result = install_bundled(&BUNDLED_AGENTS[0], &blocked);

        assert!(result.is_err());
    }

    #[test]
    fn install_surfaces_a_file_write_failure() {
        // Isolating the `write` error from the `create_dir_all` error needs a
        // layout where the directory step succeeds and only the write fails.
        // A synthetic blueprint whose second entry names a path the first entry
        // already created as a *directory* does exactly that: `create_dir_all`
        // sees an existing dir and returns Ok, then the write hits EISDIR.
        // No shipped blueprint has that shape, hence the hand-built one.
        let agent = BundledAgent {
            name: "collides-with-its-own-directory",
            version: "0.0.1",
            files: &[("tools/a.rhai", "nested first"), ("tools", "then the dir")],
        };
        let dir = tempfile::tempdir().unwrap();

        let result = install_bundled(&agent, dir.path());

        assert!(result.is_err());
    }

    #[test]
    fn install_surfaces_a_remove_failure() {
        // The destination exists but is a *file*, so `remove_dir_all` fails
        // rather than the write.
        let dir = tempfile::tempdir().unwrap();
        let agent = &BUNDLED_AGENTS[0];
        std::fs::write(dir.path().join(agent.name), "").unwrap();

        let result = install_bundled(agent, dir.path());

        assert!(result.is_err());
    }
}