use alloc::{
collections::BTreeSet,
format,
string::{String, ToString},
vec,
vec::Vec,
};
use core::fmt::Write;
use sha2::{Digest, Sha256};
use crate::{
BudgetClass, CompileError, CompileInput, EmittedFile, EmittedKind, EmittedRole, EmittedTree,
HarnessProfile, ModelResolution, Portability, RoleInput, SkillInput, TOKENIZER_VERSION,
TargetHarness, measure_text, validate_budget,
};
const REGULAR_MODE: u32 = 0o644;
const EXECUTABLE_MODE: u32 = 0o755;
const MAX_RESOURCE_BYTES: usize = 64 * 1024;
const MAX_SKILL_RESOURCE_BYTES: usize = 256 * 1024;
const PLANTER_CAPABILITIES: [&str; 7] = [
"read",
"search",
"shell",
"write",
"skill-load",
"tool-discovery",
"ask-operator",
];
const PLANTER_WRITE_SCOPE: &str =
"the bound run's mesh.md and seed.md only; native verification and seed-pointer commands";
pub fn compile(
input: &CompileInput,
profile: &HarnessProfile,
) -> Result<EmittedTree, CompileError> {
validate_input(input)?;
let mut roles = input.roles.iter().collect::<Vec<_>>();
roles.sort_by(|left, right| left.role.cmp(&right.role));
let mut skills = input.skills.iter().collect::<Vec<_>>();
skills.sort_by(|left, right| left.name.cmp(&right.name));
let mut emitted_roles = resolve_roles(&roles, &skills, profile)?;
let mut files = match profile.target {
TargetHarness::Claude => emit_claude(&roles, &emitted_roles, &skills)?,
TargetHarness::Codex => emit_codex(&roles, &emitted_roles, &skills, profile)?,
TargetHarness::Pi => emit_pi(&roles, &emitted_roles, &skills)?,
};
files.sort_by(|left, right| left.path.cmp(&right.path));
for pair in files.windows(2) {
if pair[0].path == pair[1].path {
return Err(CompileError::Invalid(format!(
"duplicate emitted path `{}` for target `{}`",
pair[0].path,
profile.target.as_str()
)));
}
}
bind_startup_skill_digests(&mut emitted_roles, &files, profile.target)?;
let skill_bundle = files
.iter()
.filter(|file| {
file.kind == EmittedKind::Skill
&& (file.path.ends_with("/SKILL.md") || file.path.contains("/references/"))
})
.map(|file| file.content.as_str())
.collect::<String>();
validate_budget(
&format!("{} skill entry set", profile.target.as_str()),
BudgetClass::HarnessSkillSet,
&skill_bundle,
)?;
let digest = tree_digest(&files);
Ok(EmittedTree {
target: profile.target,
roles: emitted_roles,
files,
digest,
tokenizer_version: TOKENIZER_VERSION,
})
}
mod digest;
mod emit;
mod resolve;
mod validate;
use digest::*;
use emit::*;
use resolve::*;
use validate::*;