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("Reasoning approach (apply before acting):\n");
306 prompt.push_str("1. Restate the exact request in one sentence to confirm understanding.\n");
307 prompt.push_str("2. Identify active constraints: conciseness requirements, hedging obligations, formatting rules, permission level.\n");
308 prompt.push_str("3. Decompose into ordered steps; flag any ambiguity or missing information before proceeding.\n");
309 prompt.push_str("4. After drafting output, self-critique: does it fully satisfy the request? Does it violate any constraint? Revise if needed.\n");
310 prompt.push_str("5. Emit only the final output — no narration of the reasoning process unless the user asked for it.\n");
311 prompt.push_str("Ordered execution phases:\n");
312 prompt.push_str(
313 "1. Inspect the current state, constraints, permissions, and dependencies before acting.\n",
314 );
315 prompt.push_str("2. Prepare only the prerequisites that are truly needed, then execute the requested work in order.\n");
316 prompt.push_str("3. Keep subtasks ordered and deduplicated; do not promote later phases until prerequisite work is actually complete.\n");
317 prompt.push_str("4. Capture concrete evidence before claiming completion.\n");
318 prompt.push_str("Verification gate:\n");
319 if request.requires_verification {
320 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");
321 } else {
322 prompt.push_str("- Before you claim success, explicitly validate the final outcome and summarize the evidence that makes the result trustworthy.\n");
323 }
324 prompt.push_str("Completion guardrails:\n");
325 prompt.push_str("- Do not skip prerequisite setup.\n");
326 prompt.push_str("- Do not mark implementation complete before verification evidence exists.\n");
327 prompt.push_str("- If a step fails, repair the plan and continue from the failed step instead of replaying completed work.\n");
328
329 if let Some(semantic_result) = semantic_result {
330 prompt.push_str("Live semantic context:\n");
331 if let Some(domain) = semantic_result.domain.as_deref() {
332 prompt.push_str(&format!("- Domain: {}\n", domain));
333 }
334 prompt.push_str(&format!("- Summary: {}\n", semantic_result.summary.trim()));
335 for hit in semantic_result.hits.iter().take(2) {
336 prompt.push_str(&format!(
337 "- Source: {} — {}\n",
338 hit.title,
339 hit.snippet.trim()
340 ));
341 }
342 }
343
344 if let Some(repair_notes) = repair_notes
345 && !repair_notes.missing_requirements.is_empty()
346 {
347 prompt.push_str("Verification repair notes:\n");
348 for note in &repair_notes.missing_requirements {
349 prompt.push_str(&format!("- {}\n", note));
350 }
351 }
352
353 if attempt > 0 {
354 prompt.push_str(&format!(
355 "Retry note:\n- This is automatic planning repair attempt {} after a verification miss.\n",
356 attempt
357 ));
358 }
359
360 prompt
361}
362
363#[cfg(feature = "advanced-primitives")]
364fn build_verification_targets(
365 request: &AdvancedPlanRequest,
366 semantic_result: Option<&semantic_client::SemanticQueryResult>,
367) -> verification::PromptVerificationTargets {
368 let mut required_headings = vec![
369 "Intent anchor:".to_string(),
370 "Reasoning approach (apply before acting):".to_string(),
371 "Ordered execution phases:".to_string(),
372 "Verification gate:".to_string(),
373 "Completion guardrails:".to_string(),
374 ];
375 if semantic_result.is_some() {
376 required_headings.push("Live semantic context:".to_string());
377 }
378
379 let mut required_phrases = vec![
380 "keep subtasks ordered and deduplicated".to_string(),
381 "do not mark implementation complete before verification evidence exists".to_string(),
382 ];
383 if request.requires_verification {
384 required_phrases.push("build/test checks".to_string());
385 }
386
387 verification::PromptVerificationTargets {
388 required_headings,
389 required_phrases,
390 require_ordered_headings: true,
391 require_verification_gate: true,
392 }
393}
394
395#[cfg(feature = "advanced-primitives")]
396fn semantic_client_config_from_hints(
397 hints: &HashMap<String, String>,
398) -> semantic_client::SemanticClientConfig {
399 let endpoint = hint_value(hints, "advanced_primitives.semantic.endpoint");
400 let api_key = hint_value(hints, "advanced_primitives.semantic.api_key");
401 let domain = hint_value(hints, "advanced_primitives.semantic.domain");
402 let enabled = hint_bool(
403 hints,
404 "advanced_primitives.semantic.enabled",
405 endpoint.is_some(),
406 );
407
408 semantic_client::SemanticClientConfig {
409 enabled,
410 endpoint,
411 api_key,
412 domain,
413 max_results: hint_usize(hints, "advanced_primitives.semantic.max_results", 3),
414 timeout_ms: hint_u64(hints, "advanced_primitives.semantic.timeout_ms", 1_500),
415 }
416}
417
418#[cfg(feature = "advanced-primitives")]
419fn verification_config_from_hints(
420 hints: &HashMap<String, String>,
421) -> verification::VerificationLoopConfig {
422 verification::VerificationLoopConfig {
423 enabled: hint_bool(hints, "advanced_primitives.verification.enabled", true),
424 max_automatic_retries: hint_u8(hints, "advanced_primitives.verification.max_retries", 2)
425 .min(2),
426 }
427}
428
429#[cfg(feature = "advanced-primitives")]
430fn bos1921_from_feedback(
431 feedback: &gestura_core_foundation::interaction::HapticFeedback,
432 semantic_hit: bool,
433) -> Bos1921Waveform {
434 Bos1921Waveform {
435 driver: "bos1921".to_string(),
436 preset: if semantic_hit {
437 "semantic-success".to_string()
438 } else {
439 "verification-success".to_string()
440 },
441 amplitude_percent: (feedback.intensity * 100.0).round().clamp(0.0, 100.0) as u8,
442 duration_ms: feedback.duration_ms,
443 repeat_count: feedback.repeat_count,
444 repeat_delay_ms: feedback.repeat_delay_ms,
445 }
446}
447
448#[cfg(feature = "advanced-primitives")]
449fn hint_value(hints: &HashMap<String, String>, key: &str) -> Option<String> {
450 hints
451 .get(key)
452 .map(|value| value.trim())
453 .filter(|value| !value.is_empty())
454 .map(ToOwned::to_owned)
455}
456
457#[cfg(feature = "advanced-primitives")]
458fn hint_bool(hints: &HashMap<String, String>, key: &str, default: bool) -> bool {
459 match hints
460 .get(key)
461 .map(|value| value.trim().to_ascii_lowercase())
462 {
463 Some(value) if matches!(value.as_str(), "1" | "true" | "yes" | "on") => true,
464 Some(value) if matches!(value.as_str(), "0" | "false" | "no" | "off") => false,
465 _ => default,
466 }
467}
468
469#[cfg(feature = "advanced-primitives")]
470fn hint_u8(hints: &HashMap<String, String>, key: &str, default: u8) -> u8 {
471 hints
472 .get(key)
473 .and_then(|value| value.trim().parse::<u8>().ok())
474 .unwrap_or(default)
475}
476
477#[cfg(feature = "advanced-primitives")]
478fn hint_u64(hints: &HashMap<String, String>, key: &str, default: u64) -> u64 {
479 hints
480 .get(key)
481 .and_then(|value| value.trim().parse::<u64>().ok())
482 .unwrap_or(default)
483}
484
485#[cfg(feature = "advanced-primitives")]
486fn hint_usize(hints: &HashMap<String, String>, key: &str, default: usize) -> usize {
487 hints
488 .get(key)
489 .and_then(|value| value.trim().parse::<usize>().ok())
490 .unwrap_or(default)
491}
492
493#[cfg(all(test, feature = "advanced-primitives"))]
494mod tests {
495 use super::*;
496
497 #[tokio::test]
498 async fn advanced_primitives_emit_waveform_and_verification_hints() {
499 let mut hints = HashMap::new();
500 hints.insert(
501 "advanced_primitives.semantic.enabled".to_string(),
502 "false".to_string(),
503 );
504
505 let outcome = AdvancedPrimitives::run_enhanced_plan(AdvancedPlanRequest {
506 user_intent: "Plan and implement the change, then verify it".to_string(),
507 base_system_prompt: "System: base".to_string(),
508 session_id: Some("session-1".to_string()),
509 task_id: Some("task-1".to_string()),
510 source: "GuiText".to_string(),
511 complex_intent: true,
512 requires_verification: true,
513 metadata_hints: hints,
514 })
515 .await;
516
517 assert!(outcome.applied);
518 assert!(outcome.system_prompt.contains("Verification gate:"));
519 assert!(
520 outcome
521 .metadata_hints
522 .contains_key("advanced_primitives.verification")
523 );
524 assert_eq!(
525 outcome
526 .bos1921_waveform
527 .as_ref()
528 .map(|waveform| waveform.driver.as_str()),
529 Some("bos1921")
530 );
531 }
532}