1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
//! Bidirectional streaming support for Plexus RPC
//!
//! This module enables **server-to-client requests** during streaming RPC execution,
//! allowing for interactive workflows like confirmations, prompts, and multi-step wizards.
//!
//! # Overview
//!
//! Traditional RPC is unidirectional: clients send requests, servers respond. Bidirectional
//! communication extends this by allowing the **server to request input from the client**
//! during stream execution. This is essential for:
//!
//! - **User confirmations** before destructive operations
//! - **Interactive prompts** for missing information
//! - **Multi-step wizards** that guide users through complex workflows
//! - **Dynamic selection menus** based on server-side state
//!
//! # Architecture
//!
//! The bidirectional system is built on generic types that can work with any
//! serializable request/response types:
//!
//! ```text
//! ┌─────────────────────────────────────────────────────────────────────┐
//! │ BidirChannel<Req, Resp> │
//! │ Generic channel for type-safe server→client requests │
//! └─────────────────────────────────────────────────────────────────────┘
//! │
//! ┌───────────────┴───────────────┐
//! ▼ ▼
//! ┌──────────────────────────────┐ ┌──────────────────────────────┐
//! │ StandardBidirChannel │ │ Custom Request/Response │
//! │ (confirm/prompt/select) │ │ (domain-specific types) │
//! └──────────────────────────────┘ └──────────────────────────────┘
//! ```
//!
//! ## Wire Format
//!
//! Bidirectional requests are sent as `PlexusStreamItem::Request`:
//!
//! ```json
//! {
//! "type": "request",
//! "requestId": "550e8400-e29b-41d4-a716-446655440000",
//! "requestData": { "type": "confirm", "message": "Delete file?" },
//! "timeoutMs": 30000
//! }
//! ```
//!
//! Clients respond via the `_plexus_respond` method or transport-specific mechanism.
//!
//! # Core Types
//!
//! - [`BidirChannel`] - Generic channel for any request/response types
//! - [`StandardBidirChannel`] - Type alias for [`BidirChannel<StandardRequest, StandardResponse>`]
//! - [`StandardRequest`] - Common UI patterns: `Confirm`, `Prompt`, `Select`
//! - [`StandardResponse`] - Matching responses: `Confirmed`, `Text`, `Selected`, `Cancelled`
//! - [`SelectOption`] - Option for selection menus
//! - [`BidirError`] - Error types for bidirectional operations
//!
//! # Helper Functions
//!
//! - [`TimeoutConfig`] - Timeout presets (quick, normal, patient, extended)
//! - [`auto_respond_channel`] - Create test channel with automatic responses
//! - [`auto_confirm_channel`] - Create test channel that auto-confirms
//! - [`bidir_error_message`] - Get user-friendly error messages
//!
//! # Examples
//!
//! ## Using StandardBidirChannel (Most Common)
//!
//! The `StandardBidirChannel` provides convenience methods for common UI patterns:
//!
//! ```rust,ignore
//! use plexus_core::plexus::bidirectional::{StandardBidirChannel, BidirError, SelectOption};
//!
//! async fn my_method(ctx: &StandardBidirChannel) -> Result<(), BidirError> {
//! // Simple yes/no confirmation
//! if ctx.confirm("Delete this file?").await? {
//! // User confirmed - proceed with deletion
//! }
//!
//! // Text input prompt
//! let name = ctx.prompt("Enter your name:").await?;
//! println!("Hello, {}!", name);
//!
//! // Selection from options
//! let choices = vec![
//! SelectOption::new("dev", "Development")
//! .with_description("Local development environment"),
//! SelectOption::new("staging", "Staging")
//! .with_description("Pre-production testing"),
//! SelectOption::new("prod", "Production")
//! .with_description("Live environment (requires approval)"),
//! ];
//! let selected = ctx.select("Choose environment:", choices).await?;
//! println!("Selected: {:?}", selected);
//!
//! Ok(())
//! }
//! ```
//!
//! ## Handling Errors Gracefully
//!
//! Always handle bidirectional errors to support non-interactive transports:
//!
//! ```rust,ignore
//! use plexus_core::plexus::bidirectional::{StandardBidirChannel, BidirError, bidir_error_message};
//!
//! async fn safe_delete(ctx: &StandardBidirChannel, path: &str) -> Result<bool, String> {
//! match ctx.confirm(&format!("Delete '{}'?", path)).await {
//! Ok(true) => {
//! // User confirmed
//! Ok(true)
//! }
//! Ok(false) => {
//! // User declined
//! Ok(false)
//! }
//! Err(BidirError::NotSupported) => {
//! // Transport doesn't support bidirectional - skip deletion for safety
//! Err("Cannot delete without user confirmation".into())
//! }
//! Err(BidirError::Cancelled) => {
//! // User explicitly cancelled
//! Ok(false)
//! }
//! Err(e) => {
//! // Other error - log and return user-friendly message
//! Err(bidir_error_message(&e))
//! }
//! }
//! }
//! ```
//!
//! ## Using Custom Request/Response Types
//!
//! For domain-specific interactions, define custom types:
//!
//! ```rust,ignore
//! use serde::{Deserialize, Serialize};
//! use schemars::JsonSchema;
//! use plexus_core::plexus::bidirectional::{BidirChannel, BidirError};
//!
//! #[derive(Serialize, Deserialize, JsonSchema)]
//! #[serde(tag = "type", rename_all = "snake_case")]
//! enum ImageRequest {
//! ConfirmOverwrite { path: String, size: u64 },
//! ChooseQuality { min: u8, max: u8, default: u8 },
//! SelectFormat { formats: Vec<String> },
//! }
//!
//! #[derive(Serialize, Deserialize, JsonSchema)]
//! #[serde(tag = "type", rename_all = "snake_case")]
//! enum ImageResponse {
//! Confirmed { value: bool },
//! Quality { value: u8 },
//! Format { value: String },
//! Cancelled,
//! }
//!
//! type ImageBidirChannel = BidirChannel<ImageRequest, ImageResponse>;
//!
//! async fn process_image(
//! ctx: &ImageBidirChannel,
//! path: &str,
//! ) -> Result<(), BidirError> {
//! // Ask for quality
//! let quality = ctx.request(ImageRequest::ChooseQuality {
//! min: 50, max: 100, default: 85,
//! }).await?;
//!
//! if let ImageResponse::Quality { value } = quality {
//! println!("Processing with quality: {}", value);
//! }
//!
//! Ok(())
//! }
//! ```
//!
//! ## Testing with Auto-Response Channels
//!
//! Use test helpers for deterministic unit tests:
//!
//! ```rust,ignore
//! use plexus_core::plexus::bidirectional::{
//! auto_respond_channel, StandardRequest, StandardResponse
//! };
//!
//! #[tokio::test]
//! async fn test_wizard_flow() {
//! let ctx = auto_respond_channel(|req: &StandardRequest| {
//! match req {
//! StandardRequest::Confirm { .. } => StandardResponse::Confirmed { value: true },
//! StandardRequest::Prompt { .. } => StandardResponse::Text { value: "test-value".into() },
//! StandardRequest::Select { options, .. } => {
//! StandardResponse::Selected { values: vec![options[0].value.clone()] }
//! }
//! }
//! });
//!
//! // Test your activation with deterministic responses
//! let result = ctx.confirm("Test?").await;
//! assert_eq!(result.unwrap(), true);
//! }
//! ```
//!
//! # Transport Support
//!
//! Bidirectional communication works differently across transports:
//!
//! | Transport | Mechanism |
//! |------------|-----------------------------------------------------|
//! | WebSocket | Request sent as stream item, response via dedicated call |
//! | MCP | Request as logging notification, response via `_plexus_respond` tool |
//! | HTTP | Not supported (stateless) |
//!
//! The `BidirChannel` automatically detects transport capabilities and returns
//! `BidirError::NotSupported` for transports that cannot handle bidirectional requests.
pub use ;
pub use ;
pub use ;
pub use ;