1use std::collections::HashMap;
36
37use serde::{Deserialize, Serialize};
38
39pub mod tasks;
40pub mod workflows;
41
42#[cfg(feature = "advanced-primitives")]
43pub mod semantic_client;
44#[cfg(feature = "advanced-primitives")]
45pub mod verification;
46
47pub use tasks::get_global_task_manager;
48
49#[cfg(feature = "advanced-primitives")]
50pub use semantic_client::{
51 SemanticClient, SemanticClientConfig, SemanticClientError, SemanticQueryHit,
52 SemanticQueryRequest, SemanticQueryResult,
53};
54#[cfg(feature = "advanced-primitives")]
55pub use verification::{
56 PromptVerificationTargets, VerificationAttempt, VerificationCheck, VerificationLoop,
57 VerificationLoopConfig, VerificationReport,
58};
59
60pub const ADVANCED_PRIMITIVES_ENABLED: bool = cfg!(feature = "advanced-primitives");
63
64#[derive(Debug, Clone, Serialize, Deserialize)]
66pub struct AdvancedPlanRequest {
67 pub user_intent: String,
69 pub base_system_prompt: String,
71 #[serde(default, skip_serializing_if = "Option::is_none")]
73 pub session_id: Option<String>,
74 #[serde(default, skip_serializing_if = "Option::is_none")]
76 pub task_id: Option<String>,
77 pub source: String,
79 #[serde(default)]
81 pub complex_intent: bool,
82 #[serde(default)]
84 pub requires_verification: bool,
85 #[serde(default, skip_serializing_if = "HashMap::is_empty")]
87 pub metadata_hints: HashMap<String, String>,
88}
89
90#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
92pub struct Bos1921Waveform {
93 pub driver: String,
95 pub preset: String,
97 pub amplitude_percent: u8,
99 pub duration_ms: u32,
101 pub repeat_count: u8,
103 pub repeat_delay_ms: u32,
105}
106
107#[derive(Debug, Clone, Serialize, Deserialize)]
109pub struct AdvancedPlanOutcome {
110 pub applied: bool,
112 pub system_prompt: String,
114 #[serde(default, skip_serializing_if = "HashMap::is_empty")]
116 pub metadata_hints: HashMap<String, String>,
117 #[serde(default, skip_serializing_if = "Option::is_none")]
119 pub bos1921_waveform: Option<Bos1921Waveform>,
120}
121
122impl AdvancedPlanOutcome {
123 pub fn passthrough(system_prompt: String) -> Self {
125 Self {
126 applied: false,
127 system_prompt,
128 metadata_hints: HashMap::new(),
129 bos1921_waveform: None,
130 }
131 }
132}
133
134pub struct AdvancedPrimitives;
136
137#[cfg(feature = "advanced-primitives")]
138impl AdvancedPrimitives {
139 pub async fn run_enhanced_plan(request: AdvancedPlanRequest) -> AdvancedPlanOutcome {
142 use gestura_core_foundation::interaction::HapticFeedback;
143
144 if !request.complex_intent {
145 return AdvancedPlanOutcome::passthrough(request.base_system_prompt);
146 }
147
148 let semantic_config = semantic_client_config_from_hints(&request.metadata_hints);
149 let verification_config = verification_config_from_hints(&request.metadata_hints);
150 let semantic_query = request
151 .metadata_hints
152 .get("advanced_primitives.semantic.query")
153 .cloned()
154 .filter(|value| !value.trim().is_empty())
155 .unwrap_or_else(|| request.user_intent.clone());
156
157 let semantic_result = if semantic_config.enabled {
158 match semantic_client::SemanticClient::new(semantic_config.clone()) {
159 Ok(client) => match client
160 .query(&semantic_client::SemanticQueryRequest {
161 query: semantic_query,
162 domain: semantic_config.domain.clone(),
163 session_id: request.session_id.clone(),
164 task_id: request.task_id.clone(),
165 source: request.source.clone(),
166 hints: request.metadata_hints.clone(),
167 })
168 .await
169 {
170 Ok(result) => result,
171 Err(error) => {
172 tracing::debug!(
173 ?error,
174 "advanced semantic query skipped after client error"
175 );
176 None
177 }
178 },
179 Err(error) => {
180 tracing::debug!(
181 ?error,
182 "advanced semantic client disabled due to invalid config"
183 );
184 None
185 }
186 }
187 } else {
188 None
189 };
190
191 let verification_targets = build_verification_targets(&request, semantic_result.as_ref());
192 let semantic_snapshot = semantic_result.clone();
193 let verification_loop = verification::VerificationLoop::new(verification_config.clone());
194 let verification_report = verification_loop
195 .run(
196 |attempt, repair_notes| {
197 let request = request.clone();
198 let semantic_snapshot = semantic_snapshot.clone();
199 let repair_notes = repair_notes.cloned();
200 async move {
201 compose_enhanced_system_prompt(
202 &request,
203 semantic_snapshot.as_ref(),
204 repair_notes.as_ref(),
205 attempt,
206 )
207 }
208 },
209 |_, candidate| {
210 let candidate = candidate.to_string();
211 let verification_targets = verification_targets.clone();
212 async move { verification::verify_prompt(&candidate, &verification_targets) }
213 },
214 )
215 .await;
216
217 let final_prompt = verification_report
218 .attempts
219 .last()
220 .map(|attempt| attempt.candidate.clone())
221 .unwrap_or_else(|| {
222 compose_enhanced_system_prompt(&request, semantic_result.as_ref(), None, 0)
223 });
224
225 let haptic_feedback = if verification_report.passed {
226 Some(HapticFeedback::success())
227 } else if semantic_result.is_some() {
228 Some(HapticFeedback::notification())
229 } else {
230 None
231 };
232 let bos1921_waveform = haptic_feedback
233 .as_ref()
234 .map(|feedback| bos1921_from_feedback(feedback, semantic_result.is_some()));
235
236 let mut metadata_hints = HashMap::from([
237 (
238 "advanced_primitives.enabled".to_string(),
239 "true".to_string(),
240 ),
241 (
242 "advanced_primitives.applied".to_string(),
243 "true".to_string(),
244 ),
245 (
246 "advanced_primitives.mode".to_string(),
247 "complex_intent".to_string(),
248 ),
249 (
250 "advanced_primitives.verification".to_string(),
251 serde_json::to_string(&verification_report).unwrap_or_else(|_| "{}".to_string()),
252 ),
253 ]);
254
255 if let Some(semantic_result) = &semantic_result {
256 metadata_hints.insert(
257 "advanced_primitives.semantic".to_string(),
258 serde_json::to_string(semantic_result).unwrap_or_else(|_| "{}".to_string()),
259 );
260 }
261 if let Some(waveform) = &bos1921_waveform {
262 metadata_hints.insert(
263 "advanced_primitives.bos1921".to_string(),
264 serde_json::to_string(waveform).unwrap_or_else(|_| "{}".to_string()),
265 );
266 }
267
268 AdvancedPlanOutcome {
269 applied: true,
270 system_prompt: final_prompt,
271 metadata_hints,
272 bos1921_waveform,
273 }
274 }
275}
276
277#[cfg(not(feature = "advanced-primitives"))]
278impl AdvancedPrimitives {
279 pub async fn run_enhanced_plan(request: AdvancedPlanRequest) -> AdvancedPlanOutcome {
282 AdvancedPlanOutcome::passthrough(request.base_system_prompt)
283 }
284}
285
286#[cfg(feature = "advanced-primitives")]
287fn compose_enhanced_system_prompt(
288 request: &AdvancedPlanRequest,
289 semantic_result: Option<&semantic_client::SemanticQueryResult>,
290 repair_notes: Option<&verification::VerificationCheck>,
291 attempt: u8,
292) -> String {
293 let mut prompt = request.base_system_prompt.clone();
294 prompt.push_str("\n\nAdvanced intent middleware:\n");
295 prompt.push_str("- The next user request is a complex multi-step intent. Preserve the original goal and keep the execution path modality-neutral across voice, chat, gesture, and future inputs.\n");
296 prompt.push_str("- Prefer existing MCP/A2A, NATS, hooks, memory-bank, and dual-orchestrator capabilities when they are the best fit instead of inventing a parallel workflow.\n");
297 prompt.push_str("Intent anchor:\n");
298 prompt.push_str(&format!(
299 "- Source: {}\n- Session: {}\n- Task: {}\n- User intent: {}\n",
300 request.source,
301 request.session_id.as_deref().unwrap_or("n/a"),
302 request.task_id.as_deref().unwrap_or("n/a"),
303 request.user_intent.trim()
304 ));
305 prompt.push_str("Ordered execution phases:\n");
306 prompt.push_str(
307 "1. Inspect the current state, constraints, permissions, and dependencies before acting.\n",
308 );
309 prompt.push_str("2. Prepare only the prerequisites that are truly needed, then execute the requested work in order.\n");
310 prompt.push_str("3. Keep subtasks ordered and deduplicated; do not promote later phases until prerequisite work is actually complete.\n");
311 prompt.push_str("4. Capture concrete evidence before claiming completion.\n");
312 prompt.push_str("Verification gate:\n");
313 if request.requires_verification {
314 prompt.push_str("- Before you claim success, explicitly run or request the right verification steps, build/test checks, or equivalent domain validation and summarize the observed evidence.\n");
315 } else {
316 prompt.push_str("- Before you claim success, explicitly validate the final outcome and summarize the evidence that makes the result trustworthy.\n");
317 }
318 prompt.push_str("Completion guardrails:\n");
319 prompt.push_str("- Do not skip prerequisite setup.\n");
320 prompt.push_str("- Do not mark implementation complete before verification evidence exists.\n");
321 prompt.push_str("- If a step fails, repair the plan and continue from the failed step instead of replaying completed work.\n");
322
323 if let Some(semantic_result) = semantic_result {
324 prompt.push_str("Live semantic context:\n");
325 if let Some(domain) = semantic_result.domain.as_deref() {
326 prompt.push_str(&format!("- Domain: {}\n", domain));
327 }
328 prompt.push_str(&format!("- Summary: {}\n", semantic_result.summary.trim()));
329 for hit in semantic_result.hits.iter().take(2) {
330 prompt.push_str(&format!(
331 "- Source: {} — {}\n",
332 hit.title,
333 hit.snippet.trim()
334 ));
335 }
336 }
337
338 if let Some(repair_notes) = repair_notes
339 && !repair_notes.missing_requirements.is_empty()
340 {
341 prompt.push_str("Verification repair notes:\n");
342 for note in &repair_notes.missing_requirements {
343 prompt.push_str(&format!("- {}\n", note));
344 }
345 }
346
347 if attempt > 0 {
348 prompt.push_str(&format!(
349 "Retry note:\n- This is automatic planning repair attempt {} after a verification miss.\n",
350 attempt
351 ));
352 }
353
354 prompt
355}
356
357#[cfg(feature = "advanced-primitives")]
358fn build_verification_targets(
359 request: &AdvancedPlanRequest,
360 semantic_result: Option<&semantic_client::SemanticQueryResult>,
361) -> verification::PromptVerificationTargets {
362 let mut required_headings = vec![
363 "Intent anchor:".to_string(),
364 "Ordered execution phases:".to_string(),
365 "Verification gate:".to_string(),
366 "Completion guardrails:".to_string(),
367 ];
368 if semantic_result.is_some() {
369 required_headings.push("Live semantic context:".to_string());
370 }
371
372 let mut required_phrases = vec![
373 "keep subtasks ordered and deduplicated".to_string(),
374 "do not mark implementation complete before verification evidence exists".to_string(),
375 ];
376 if request.requires_verification {
377 required_phrases.push("build/test checks".to_string());
378 }
379
380 verification::PromptVerificationTargets {
381 required_headings,
382 required_phrases,
383 require_ordered_headings: true,
384 require_verification_gate: true,
385 }
386}
387
388#[cfg(feature = "advanced-primitives")]
389fn semantic_client_config_from_hints(
390 hints: &HashMap<String, String>,
391) -> semantic_client::SemanticClientConfig {
392 let endpoint = hint_value(hints, "advanced_primitives.semantic.endpoint");
393 let api_key = hint_value(hints, "advanced_primitives.semantic.api_key");
394 let domain = hint_value(hints, "advanced_primitives.semantic.domain");
395 let enabled = hint_bool(
396 hints,
397 "advanced_primitives.semantic.enabled",
398 endpoint.is_some(),
399 );
400
401 semantic_client::SemanticClientConfig {
402 enabled,
403 endpoint,
404 api_key,
405 domain,
406 max_results: hint_usize(hints, "advanced_primitives.semantic.max_results", 3),
407 timeout_ms: hint_u64(hints, "advanced_primitives.semantic.timeout_ms", 1_500),
408 }
409}
410
411#[cfg(feature = "advanced-primitives")]
412fn verification_config_from_hints(
413 hints: &HashMap<String, String>,
414) -> verification::VerificationLoopConfig {
415 verification::VerificationLoopConfig {
416 enabled: hint_bool(hints, "advanced_primitives.verification.enabled", true),
417 max_automatic_retries: hint_u8(hints, "advanced_primitives.verification.max_retries", 2)
418 .min(2),
419 }
420}
421
422#[cfg(feature = "advanced-primitives")]
423fn bos1921_from_feedback(
424 feedback: &gestura_core_foundation::interaction::HapticFeedback,
425 semantic_hit: bool,
426) -> Bos1921Waveform {
427 Bos1921Waveform {
428 driver: "bos1921".to_string(),
429 preset: if semantic_hit {
430 "semantic-success".to_string()
431 } else {
432 "verification-success".to_string()
433 },
434 amplitude_percent: (feedback.intensity * 100.0).round().clamp(0.0, 100.0) as u8,
435 duration_ms: feedback.duration_ms,
436 repeat_count: feedback.repeat_count,
437 repeat_delay_ms: feedback.repeat_delay_ms,
438 }
439}
440
441#[cfg(feature = "advanced-primitives")]
442fn hint_value(hints: &HashMap<String, String>, key: &str) -> Option<String> {
443 hints
444 .get(key)
445 .map(|value| value.trim())
446 .filter(|value| !value.is_empty())
447 .map(ToOwned::to_owned)
448}
449
450#[cfg(feature = "advanced-primitives")]
451fn hint_bool(hints: &HashMap<String, String>, key: &str, default: bool) -> bool {
452 match hints
453 .get(key)
454 .map(|value| value.trim().to_ascii_lowercase())
455 {
456 Some(value) if matches!(value.as_str(), "1" | "true" | "yes" | "on") => true,
457 Some(value) if matches!(value.as_str(), "0" | "false" | "no" | "off") => false,
458 _ => default,
459 }
460}
461
462#[cfg(feature = "advanced-primitives")]
463fn hint_u8(hints: &HashMap<String, String>, key: &str, default: u8) -> u8 {
464 hints
465 .get(key)
466 .and_then(|value| value.trim().parse::<u8>().ok())
467 .unwrap_or(default)
468}
469
470#[cfg(feature = "advanced-primitives")]
471fn hint_u64(hints: &HashMap<String, String>, key: &str, default: u64) -> u64 {
472 hints
473 .get(key)
474 .and_then(|value| value.trim().parse::<u64>().ok())
475 .unwrap_or(default)
476}
477
478#[cfg(feature = "advanced-primitives")]
479fn hint_usize(hints: &HashMap<String, String>, key: &str, default: usize) -> usize {
480 hints
481 .get(key)
482 .and_then(|value| value.trim().parse::<usize>().ok())
483 .unwrap_or(default)
484}
485
486#[cfg(all(test, feature = "advanced-primitives"))]
487mod tests {
488 use super::*;
489
490 #[tokio::test]
491 async fn advanced_primitives_emit_waveform_and_verification_hints() {
492 let mut hints = HashMap::new();
493 hints.insert(
494 "advanced_primitives.semantic.enabled".to_string(),
495 "false".to_string(),
496 );
497
498 let outcome = AdvancedPrimitives::run_enhanced_plan(AdvancedPlanRequest {
499 user_intent: "Plan and implement the change, then verify it".to_string(),
500 base_system_prompt: "System: base".to_string(),
501 session_id: Some("session-1".to_string()),
502 task_id: Some("task-1".to_string()),
503 source: "GuiText".to_string(),
504 complex_intent: true,
505 requires_verification: true,
506 metadata_hints: hints,
507 })
508 .await;
509
510 assert!(outcome.applied);
511 assert!(outcome.system_prompt.contains("Verification gate:"));
512 assert!(
513 outcome
514 .metadata_hints
515 .contains_key("advanced_primitives.verification")
516 );
517 assert_eq!(
518 outcome
519 .bos1921_waveform
520 .as_ref()
521 .map(|waveform| waveform.driver.as_str()),
522 Some("bos1921")
523 );
524 }
525}