///|
/// Canonical tool-definition types for the M1 functional Kernel.
///
/// ADR §2.7, §2.8, §2.11: the catalog snapshot is the single source of truth
/// for tool definitions (model-visible) AND for execution owners. The
/// reducer/executor never re-resolves owners from a separate routing cache.
/// Tool execution policy is explicit data — not inferred from the tool name.
///|
/// How a tool may be batched with other tools in the same model completion.
///
/// - `Sequential`: occupies a single-element wave. Safe default; file/shell
/// side effects never race.
/// - `Parallel`: may share a wave with adjacent `Parallel` calls.
/// - `Exclusive`: forms a barrier before and after; never shares a wave.
///
/// `Sequential` is the safe default. Products opt into `Parallel` only for
/// tools whose effects are independently safe, and use `Exclusive` at a
/// visible side-effect boundary.
pub(all) enum ExecutionPolicy {
Sequential
Parallel
Exclusive
} derive(Eq, Debug)
///|
pub impl Show for ExecutionPolicy with fn to_string(self : ExecutionPolicy) -> String {
match self {
Sequential => "Sequential"
Parallel => "Parallel"
Exclusive => "Exclusive"
}
}
///|
/// Stable identifier for the provider that owns a tool. The Kernel uses this
/// only for owner lookup in the runtime registry; it does not call into the
/// owner through this type.
pub(all) struct OwnerId {
value : String
} derive(Eq, Hash, Debug)
///|
pub fn OwnerId::new(value : String) -> Result[OwnerId, String] {
if value == "" {
Err("OwnerId must be non-empty")
} else {
Ok(OwnerId::{ value, })
}
}
///|
pub fn OwnerId::unchecked(value : String) -> OwnerId {
OwnerId::{ value, }
}
///|
pub fn OwnerId::to_string(self : OwnerId) -> String {
self.value
}
///|
/// A single tool's definition as the model and the executor see it. Owned by
/// the catalog snapshot (T05), not by individual providers.
pub(all) struct ToolDef {
name : ToolName
description : String
input_schema : Json
owner : OwnerId
policy : ExecutionPolicy
/// Optional provenance label (e.g. provider name, MCP server id). Included
/// in collision diagnostics; never used for routing decisions.
provenance : String?
} derive(Eq, Debug)
///|
/// Internal builder used by the catalog. Most fields are required; provenance
/// is optional. The catalog builder (T05) validates the schema and policy
/// before constructing this, so by the time a `ToolDef` reaches the
/// reducer, it has already passed composition.
pub fn ToolDef::ToolDef(
name~ : ToolName,
description~ : String,
input_schema~ : Json,
owner~ : OwnerId,
policy~ : ExecutionPolicy,
provenance~ : String?,
) -> ToolDef {
{ name, description, input_schema, owner, policy, provenance }
}
///|
pub impl Show for ToolDef with fn to_string(self : ToolDef) -> String {
"Tool(\{self.name.to_string()} @ \{self.owner.to_string()}, policy=\{self.policy.to_string()})"
}
///|
/// Result of validating parsed tool arguments against a `ToolDef`'s
/// input schema. The validator (T05) returns this; the reducer (T03) maps
/// `Err` into a `ToolOutcome::NotExecuted(SchemaMismatch)` carrying the
/// original call id.
///
/// The diagnostic deliberately excludes the raw argument payload; it reports
/// only a JSON path and a type expectation, so that prompts and tool arguments
/// never leak into logs.
pub(all) struct SchemaDiagnostic {
json_path : String
expected : String
actual : String
} derive(Eq, Debug)
///|
pub impl Show for SchemaDiagnostic with fn to_string(self : SchemaDiagnostic) -> String {
"SchemaDiagnostic(path=\{self.json_path}, expected=\{self.expected}, actual=\{self.actual})"
}