blob: 4dc7282bb30e2db4b27d6d681a565d7e9abaf825 [file]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
use serde::{Deserialize, Serialize};
use sha2::{Digest, Sha256};
/// Stable Host tool-use identity shared by client callbacks and presentation.
/// Provider IDs remain step-scoped model evidence and may not be wire-safe.
pub fn tool_use_id(invocation_id: &str, operation_id: &str) -> String {
let bytes =
serde_json::to_vec(&["maka.tool-presentation.v1", invocation_id, operation_id]).unwrap();
format!("tool_{}", hex::encode(Sha256::digest(bytes)))
}
pub fn provider_result_id(event_id: &str, part_index: usize) -> String {
format!("{event_id}_tool_result_{part_index}")
}
pub fn metered_usage_id(event_id: &str) -> String {
format!("{event_id}:usage")
}
pub fn parse_metered_usage_id(id: &str) -> Option<&str> {
id.strip_suffix(":usage")
}
pub fn parse_provider_result_id(id: &str) -> Option<(&str, usize)> {
let (event, index) = id.rsplit_once("_tool_result_")?;
let index: usize = index.parse().ok()?;
(provider_result_id(event, index) == id).then_some((event, index))
}
/// Recorded Host settlement for presentation; returned is not business success.
/// Foreign/provider results do not establish this Host evidence.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum ToolSettlement {
Returned,
Failed,
Unknown,
Rejected,
}
/// Refusal category without input-derived explanation text.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum RejectionKind {
Unavailable,
InvalidInput,
PolicyDenied,
PreparationFailed,
ExclusiveConflict,
Cancelled,
}
/// A known refusal before durable dispatch: no tool effect was admitted.
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, thiserror::Error)]
#[serde(tag = "kind", rename_all = "snake_case")]
pub enum ToolRejection {
#[error("tool is unavailable")]
Unavailable,
#[error("invalid tool input: {message}")]
InvalidInput { message: String },
#[error("tool policy denied: {message}")]
PolicyDenied { message: String },
#[error("tool preparation failed: {message}")]
PreparationFailed { message: String },
#[error("tool conflicts with exclusive step execution")]
ExclusiveConflict,
#[error("tool cancelled before dispatch; tool was not executed")]
Cancelled,
}
/// A Tool Call is distinct from its durable host operation. Origin determines
/// model visibility; it is not an independent flag that can contradict ancestry.
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
pub struct ToolCallIdentity {
pub tool_call_id: String,
pub origin: ToolOrigin,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
#[serde(tag = "kind", rename_all = "snake_case")]
pub enum ToolOrigin {
Provider {
step_id: String,
},
CodeMode {
parent_operation_id: String,
parent_tool_call_id: String,
},
/// An independently running cell accepted by an exec control call.
/// Its parent may settle once startup/observation completes; its own nested
/// tool effects must still drain before the cell's canonical settlement.
CodeCell {
parent_operation_id: String,
parent_tool_call_id: String,
},
Standalone,
/// Explicit Host SDK work, never dispatch inferred from executor observations.
HostSdk {
package_id: String,
entry_id: String,
activation: String,
parent_operation_id: Option<String>,
},
}
impl ToolCallIdentity {
pub fn provider(step_id: String, tool_call_id: String) -> Self {
Self {
tool_call_id,
origin: ToolOrigin::Provider { step_id },
}
}
pub fn standalone(tool_call_id: String) -> Self {
Self {
tool_call_id,
origin: ToolOrigin::Standalone,
}
}
}