lean_ctx/core/ocla/adapters/
passthrough.rs1use std::path::{Path, PathBuf};
4use std::sync::OnceLock;
5use std::time::Instant;
6
7use lean_ctx_protocol::CapabilityManifestV1;
8
9use super::{invoke_passthrough_text, read_context_paths};
10use crate::core::ocla::invocation::{
11 CapabilityAdapter, CapabilityInput, CapabilityInvocation, CapabilityResult,
12};
13use crate::core::ocla::{OclaError, OclaResult};
14
15pub const MANIFEST_PATH: &str =
17 "docs/contracts/ocla/capability-manifests/leanctx/passthrough-v1.json";
18const MANIFEST_JSON: &str = include_str!(concat!(
19 env!("CARGO_MANIFEST_DIR"),
20 "/assets/ocla/capability-manifests/leanctx/passthrough-v1.json"
21));
22
23fn manifest() -> &'static CapabilityManifestV1 {
24 static MANIFEST: OnceLock<CapabilityManifestV1> = OnceLock::new();
25 MANIFEST.get_or_init(|| {
26 let manifest: CapabilityManifestV1 =
27 serde_json::from_str(MANIFEST_JSON).expect("passthrough manifest is valid JSON");
28 manifest
29 .validate()
30 .expect("passthrough manifest satisfies the protocol");
31 manifest
32 })
33}
34
35pub struct PassthroughAdapter {
37 root: PathBuf,
38}
39
40impl PassthroughAdapter {
41 #[must_use]
42 pub fn new() -> Self {
43 Self {
44 root: std::env::current_dir().unwrap_or_else(|_| PathBuf::from(".")),
45 }
46 }
47
48 #[must_use]
49 pub fn with_root(root: impl AsRef<Path>) -> Self {
50 Self {
51 root: root.as_ref().to_path_buf(),
52 }
53 }
54
55 #[must_use]
56 pub fn root(&self) -> &Path {
57 &self.root
58 }
59}
60
61impl Default for PassthroughAdapter {
62 fn default() -> Self {
63 Self::new()
64 }
65}
66
67impl CapabilityAdapter for PassthroughAdapter {
68 fn manifest(&self) -> &CapabilityManifestV1 {
69 manifest()
70 }
71
72 fn invoke(&self, invocation: CapabilityInvocation) -> OclaResult<CapabilityResult> {
73 invocation.validate()?;
74 if invocation.capability_id != self.manifest().capability_id.as_str()
75 || invocation.capability_version != self.manifest().version
76 {
77 return Err(OclaError::InvalidRequest(
78 "invocation capability identity does not match adapter manifest".into(),
79 ));
80 }
81 let start = Instant::now();
82 match invocation.input.clone() {
83 CapabilityInput::ContextRequest { paths, .. } => {
84 let content = read_context_paths(&self.root, &paths)?;
85 invoke_passthrough_text(&invocation, &content, start)
86 }
87 CapabilityInput::ShellCommand { command, .. } => {
88 invoke_passthrough_text(&invocation, &command, start)
89 }
90 CapabilityInput::ModelRequest { prompt, .. } => {
91 invoke_passthrough_text(&invocation, &prompt, start)
92 }
93 CapabilityInput::AgentTask(_) => Err(OclaError::InvalidRequest(
94 "passthrough cannot execute agent tasks".into(),
95 )),
96 }
97 }
98
99 fn health_check(&self) -> OclaResult<bool> {
100 Ok(self.root.is_dir() && self.manifest().validate().is_ok())
101 }
102}
103
104#[cfg(test)]
105mod tests {
106 use super::*;
107 use crate::core::ocla::invocation::{CapabilityAdapter, PolicyConstraints};
108
109 #[test]
110 fn preserves_shell_command_content_reference() {
111 let adapter = PassthroughAdapter::new();
112 let invocation = CapabilityInvocation {
113 task_id: "task-1".into(),
114 capability_id: adapter.manifest().capability_id.as_str().into(),
115 capability_version: adapter.manifest().version.clone(),
116 input: CapabilityInput::ShellCommand {
117 command: "printf unchanged".into(),
118 workdir: None,
119 },
120 policy_constraints: PolicyConstraints::default(),
121 timeout_ms: 0,
123 };
124 let result = adapter.invoke(invocation).expect("passthrough invocation");
125 assert!(result.success);
126 assert_eq!(result.observation.input_tokens, result.output_tokens);
127 assert!(result.evidence_ref.is_some());
128 }
129}