Skip to main content

kcode_k1_codex_runtime/
lib.rs

1pub use kcode_k1_codex_conversations::{
2    Config, DynamicTool, Error, ErrorKind, Event, ToolCall, ToolResult,
3};
4use kcode_k1_codex_conversations::{
5    Diagnostics, Pending, State, ToolToken, parse_rpc_id, thread_start_params, turn_start_params,
6};
7use kcode_k1_codex_events::{
8    DynamicToolCall as DecodedToolCall, Inbound, ResponseOutcome, RpcId, ScopedEvent, ScopedKind,
9    ServerError, TurnCompleted, TurnStatus, decode, rejection_payload,
10};
11use kcode_k1_codex_transport::{
12    ClientInfo, Config as WireConfig, Error as WireError, ErrorKind as WireKind, Transport,
13};
14use serde_json::{Value, json};
15use std::{
16    fmt,
17    sync::{Arc, Weak},
18    time::Duration,
19};
20use tokio::sync::{mpsc, oneshot};
21const EARLY_LIMIT: usize = 1024;
22type Events = mpsc::UnboundedSender<Event>;
23type Reply = oneshot::Sender<Result<(), Error>>;
24type CloseReply = Option<Reply>;
25type StartReply = oneshot::Sender<Result<u64, Error>>;
26enum Command {
27    Start {
28        key: String,
29        input: String,
30        events: Events,
31        reply: StartReply,
32    },
33    Respond {
34        key: String,
35        turn: u64,
36        call: String,
37        result: ToolResult,
38        reply: Reply,
39    },
40    Abandon {
41        key: String,
42        turn: u64,
43    },
44    Close {
45        key: String,
46        reply: Reply,
47    },
48}
49struct Client {
50    commands: mpsc::UnboundedSender<Command>,
51    diagnostics: Diagnostics,
52}
53#[derive(Clone)]
54pub struct Adapter {
55    client: Arc<Client>,
56}
57impl Adapter {
58    pub async fn open(config: Config) -> Result<Self, Error> {
59        open(config).await
60    }
61    pub async fn start_turn(
62        &self,
63        conversation_key: impl Into<String>,
64        input: impl Into<String>,
65    ) -> Result<Turn, Error> {
66        let key = conversation_key.into();
67        let (events, receiver) = mpsc::unbounded_channel();
68        let (reply, answer) = oneshot::channel();
69        self.client
70            .commands
71            .send(Command::Start {
72                key: key.clone(),
73                input: input.into(),
74                events,
75                reply,
76            })
77            .map_err(|_| self.unavailable())?;
78        let serial = answer.await.map_err(|_| self.unavailable())??;
79        Ok(Turn {
80            key,
81            serial,
82            client: Arc::downgrade(&self.client),
83            events: receiver,
84            diagnostics: self.client.diagnostics.clone(),
85            terminal: false,
86        })
87    }
88    pub async fn close_conversation(&self, key: impl Into<String>) -> Result<(), Error> {
89        let (reply, answer) = oneshot::channel();
90        self.client
91            .commands
92            .send(Command::Close {
93                key: key.into(),
94                reply,
95            })
96            .map_err(|_| self.unavailable())?;
97        answer.await.map_err(|_| self.unavailable())?
98    }
99    pub fn diagnostics(&self) -> Vec<u8> {
100        self.client.diagnostics.snapshot()
101    }
102    fn unavailable(&self) -> Error {
103        self.client
104            .diagnostics
105            .error(ErrorKind::Unavailable, "Codex app-server is unavailable")
106    }
107}
108pub struct Turn {
109    key: String,
110    serial: u64,
111    client: Weak<Client>,
112    events: mpsc::UnboundedReceiver<Event>,
113    diagnostics: Diagnostics,
114    terminal: bool,
115}
116impl fmt::Debug for Turn {
117    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
118        formatter
119            .debug_struct("Turn")
120            .field("key", &self.key)
121            .field("serial", &self.serial)
122            .finish_non_exhaustive()
123    }
124}
125impl Turn {
126    pub async fn next_event(&mut self) -> Option<Event> {
127        let event = self.events.recv().await;
128        if event
129            .as_ref()
130            .is_none_or(|event| matches!(event, Event::Done | Event::Error(_)))
131        {
132            self.terminal = true;
133        }
134        event
135    }
136    pub async fn respond(
137        &self,
138        call_id: impl Into<String>,
139        result: ToolResult,
140    ) -> Result<(), Error> {
141        let client = self.client.upgrade().ok_or_else(|| self.unavailable())?;
142        let (reply, answer) = oneshot::channel();
143        client
144            .commands
145            .send(Command::Respond {
146                key: self.key.clone(),
147                turn: self.serial,
148                call: call_id.into(),
149                result,
150                reply,
151            })
152            .map_err(|_| self.unavailable())?;
153        answer.await.map_err(|_| self.unavailable())?
154    }
155    fn unavailable(&self) -> Error {
156        self.diagnostics
157            .error(ErrorKind::Unavailable, "Codex app-server is unavailable")
158    }
159}
160impl Drop for Turn {
161    fn drop(&mut self) {
162        if !self.terminal
163            && let Some(client) = self.client.upgrade()
164        {
165            let _ = client.commands.send(Command::Abandon {
166                key: self.key.clone(),
167                turn: self.serial,
168            });
169        }
170    }
171}
172struct Actor {
173    transport: Transport,
174    commands: mpsc::UnboundedReceiver<Command>,
175    config: Config,
176    diagnostics: Diagnostics,
177    state: State<Events, CloseReply>,
178}
179async fn open(config: Config) -> Result<Adapter, Error> {
180    config.validate()?;
181    let mut wire = WireConfig::new(config.executable.clone(), config.working_directory.clone());
182    wire.reasoning_effort = config.reasoning_effort.clone();
183    wire.client = ClientInfo {
184        name: "kcode-k1-codex-adapter".into(),
185        title: "K1 Codex Adapter".into(),
186        version: "0.2.1".into(),
187    };
188    let transport = Transport::open(wire).await.map_err(map_wire)?;
189    let diagnostics = Diagnostics::new(transport.diagnostics());
190    let (commands, receiver) = mpsc::unbounded_channel();
191    let client = Arc::new(Client {
192        commands,
193        diagnostics: diagnostics.clone(),
194    });
195    let mut actor = Actor {
196        transport,
197        commands: receiver,
198        config,
199        diagnostics,
200        state: State::default(),
201    };
202    tokio::spawn(async move {
203        let outcome = actor.run().await;
204        actor.refresh();
205        let mut error = outcome
206            .err()
207            .unwrap_or_else(|| actor.error(ErrorKind::Unavailable, "Codex app-server was closed"));
208        let _ = actor.reject_all("adapter stopped").await;
209        let sinks = actor.state.take_sinks();
210        actor.refresh();
211        error.diagnostics = actor.diagnostics.snapshot();
212        for sink in sinks {
213            let _ = sink.send(Event::Error(error.clone()));
214        }
215        let _ = actor.transport.close().await;
216        actor.refresh();
217    });
218    Ok(Adapter { client })
219}
220impl Actor {
221    async fn run(&mut self) -> Result<(), Error> {
222        let mut tick = tokio::time::interval(Duration::from_millis(20));
223        loop {
224            tokio::select! {
225                command = self.commands.recv() => match command {
226                    Some(command) => self.command(command).await?,
227                    None => return Ok(()),
228                },
229                message = self.transport.receive() => self.message(message.map_err(map_wire)?).await?,
230                _ = tick.tick() => self.refresh(),
231            }
232        }
233    }
234    async fn command(&mut self, command: Command) -> Result<(), Error> {
235        self.refresh();
236        match command {
237            Command::Start {
238                key,
239                input,
240                events,
241                reply,
242            } => {
243                if self.start_pending(&key) {
244                    let _ = reply.send(Err(
245                        self.error(ErrorKind::Busy, "conversation already has an active turn")
246                    ));
247                    return Ok(());
248                }
249                let serial = match self.state.begin_turn(key.clone(), events) {
250                    Ok(serial) => serial,
251                    Err(error) => {
252                        let _ = reply.send(Err(self.stamp(error)));
253                        return Ok(());
254                    }
255                };
256                let request = if let Some(thread) = self.state.thread(&key).map(str::to_owned) {
257                    self.request(
258                        "turn/start",
259                        turn_start_params(&thread, input),
260                        Pending::Turn {
261                            key: key.clone(),
262                            serial,
263                        },
264                    )
265                    .await
266                } else {
267                    self.request(
268                        "thread/start",
269                        thread_start_params(&self.config),
270                        Pending::Thread {
271                            key: key.clone(),
272                            serial,
273                            input,
274                        },
275                    )
276                    .await
277                };
278                if let Err(error) = request {
279                    self.state.take_active(&key, serial);
280                    let _ = reply.send(Err(error.clone()));
281                    return Err(error);
282                }
283                if reply.send(Ok(serial)).is_err()
284                    && let Some(active) = self.state.take_active(&key, serial)
285                {
286                    self.reject_early(active.early, "turn start receiver was dropped")
287                        .await?;
288                }
289            }
290            Command::Respond {
291                key,
292                turn,
293                call,
294                result,
295                reply,
296            } => {
297                let token: ToolToken = (key, turn, call);
298                let Some(pending) = self.state.take_tool(&token) else {
299                    let _ = reply.send(Err(self.error(
300                        ErrorKind::InvalidToolResult,
301                        "tool call is not pending on this turn",
302                    )));
303                    return Ok(());
304                };
305                let message = json!({"id": pending.id, "result": {"contentItems": [{"type": "inputText", "text": result.output}], "success": result.success}});
306                if let Err(error) = self.transport.send(&message).await.map_err(map_wire) {
307                    let _ = reply.send(Err(error.clone()));
308                    return Err(error);
309                }
310                let _ = reply.send(Ok(()));
311            }
312            Command::Abandon { key, turn } => self.abandon(&key, turn).await?,
313            Command::Close { key, reply } => {
314                if self.start_pending(&key) {
315                    let _ = reply.send(Err(
316                        self.error(ErrorKind::Busy, "conversation is active or already closing")
317                    ));
318                    return Ok(());
319                }
320                let thread = match self.state.begin_close(&key) {
321                    Ok(Some(thread)) => thread,
322                    Ok(None) => {
323                        let _ = reply.send(Ok(()));
324                        return Ok(());
325                    }
326                    Err(error) => {
327                        let _ = reply.send(Err(self.stamp(error)));
328                        return Ok(());
329                    }
330                };
331                if let Err(error) = self
332                    .request(
333                        "thread/unsubscribe",
334                        json!({"threadId": thread}),
335                        Pending::Close {
336                            key: key.clone(),
337                            thread,
338                            reply: Some(reply),
339                        },
340                    )
341                    .await
342                {
343                    self.state.cancel_close(&key);
344                    return Err(error);
345                }
346            }
347        }
348        Ok(())
349    }
350    async fn message(&mut self, message: Value) -> Result<(), Error> {
351        self.refresh();
352        match decode(message.clone()) {
353            Inbound::ClientResponse(response) => self.response(response.id, response.outcome).await,
354            Inbound::Scoped(event) => self.scoped(event, message).await,
355            Inbound::ResolvedRequest(id) => {
356                self.state
357                    .resolve_tool(&id.as_value())
358                    .map_err(|error| self.stamp(error))?;
359                Ok(())
360            }
361            Inbound::GlobalServerError(error) => Err(error.to_error(self.transport.diagnostics())),
362            Inbound::ModelReroute(reroute) => {
363                if let Some(id) = reroute.id {
364                    self.reject(&id, -32602, "model rerouting is unsupported")
365                        .await?;
366                }
367                Err(self.error(ErrorKind::Protocol, "model rerouting is unsupported"))
368            }
369            Inbound::UnsupportedServerRequest(request) => {
370                self.reject(&request.id, -32601, "unsupported server request")
371                    .await
372            }
373            Inbound::IgnoredNotification { .. } => Ok(()),
374            Inbound::Malformed(error) => self.malformed(&message, error).await,
375        }
376    }
377    async fn malformed(&mut self, message: &Value, error: Error) -> Result<(), Error> {
378        if message.get("method").and_then(Value::as_str) == Some("item/tool/call")
379            && let Some(id) = message.get("id")
380            && let Ok(id) = RpcId::try_from(id)
381        {
382            self.reject(&id, -32602, "malformed dynamic tool request")
383                .await?;
384        }
385        Err(self.stamp(error))
386    }
387    async fn response(&mut self, id: u64, outcome: ResponseOutcome) -> Result<(), Error> {
388        let pending = self
389            .state
390            .take_pending(id)
391            .map_err(|error| self.stamp(error))?;
392        let result = match outcome {
393            ResponseOutcome::Result(result) => result,
394            ResponseOutcome::Error(error) => {
395                return self
396                    .pending_failed(pending, error.to_error(self.transport.diagnostics()))
397                    .await;
398            }
399        };
400        match pending {
401            Pending::Thread { key, serial, input } => {
402                let Some(thread) = result
403                    .pointer("/thread/id")
404                    .and_then(Value::as_str)
405                    .map(str::to_owned)
406                else {
407                    let error = self.error(
408                        ErrorKind::Protocol,
409                        "thread/start response omitted thread.id",
410                    );
411                    return self.prestart_fail(&key, serial, error).await;
412                };
413                let live = match self
414                    .state
415                    .conversations
416                    .get(&key)
417                    .and_then(|conversation| conversation.active.as_ref())
418                {
419                    Some(active) if active.serial == serial => !active.cancelled,
420                    Some(_) => {
421                        return Err(self.error(
422                            ErrorKind::Protocol,
423                            "thread/start response crossed conversation turns",
424                        ));
425                    }
426                    None => false,
427                };
428                if let Err(error) = self.state.set_thread(&key, thread.clone()) {
429                    let error = self.stamp(error);
430                    return self.prestart_fail(&key, serial, error).await;
431                }
432                if !live {
433                    if let Some(active) = self.state.take_active(&key, serial) {
434                        self.reject_early(active.early, "turn was abandoned")
435                            .await?;
436                    }
437                    return self.retire_thread(&key).await;
438                }
439                self.request(
440                    "turn/start",
441                    turn_start_params(&thread, input),
442                    Pending::Turn { key, serial },
443                )
444                .await?;
445            }
446            Pending::Turn { key, serial } => {
447                let Some(turn) = result
448                    .pointer("/turn/id")
449                    .and_then(Value::as_str)
450                    .map(str::to_owned)
451                else {
452                    let error =
453                        self.error(ErrorKind::Protocol, "turn/start response omitted turn.id");
454                    return self.prestart_fail(&key, serial, error).await;
455                };
456                let Some(early) = self.state.set_native_turn(&key, serial, turn.clone()) else {
457                    return self.retire_turn(&key, &turn).await;
458                };
459                for message in early {
460                    match decode(message.clone()) {
461                        Inbound::Scoped(event) => self.scoped(event, message).await?,
462                        Inbound::Malformed(error) => return Err(self.stamp(error)),
463                        _ => {
464                            return Err(self.error(
465                                ErrorKind::Protocol,
466                                "buffered event changed classification",
467                            ));
468                        }
469                    }
470                }
471                self.interrupt(&key, serial).await?;
472            }
473            Pending::Close { key, thread, reply } => {
474                if let Err(error) = self.state.finish_close(&key, &thread) {
475                    let error = self.stamp(error);
476                    if let Some(reply) = reply {
477                        let _ = reply.send(Err(error.clone()));
478                    }
479                    return Err(error);
480                }
481                if let Some(reply) = reply {
482                    let _ = reply.send(Ok(()));
483                }
484            }
485            Pending::Interrupt => {}
486        }
487        Ok(())
488    }
489    async fn pending_failed(
490        &mut self,
491        pending: Pending<CloseReply>,
492        error: Error,
493    ) -> Result<(), Error> {
494        match pending {
495            Pending::Thread { key, serial, .. } | Pending::Turn { key, serial } => {
496                self.prestart_fail(&key, serial, error).await
497            }
498            Pending::Close { key, reply, .. } => {
499                self.state.cancel_close(&key);
500                if let Some(reply) = reply {
501                    let _ = reply.send(Err(error));
502                }
503                Ok(())
504            }
505            Pending::Interrupt => Err(error),
506        }
507    }
508    async fn scoped(&mut self, event: ScopedEvent, message: Value) -> Result<(), Error> {
509        let ScopedEvent { scope, kind } = event;
510        let Some(key) = self.state.owner(&scope.thread_id).map(str::to_owned) else {
511            self.reject_if_tool(&kind, "unknown thread").await?;
512            return Ok(());
513        };
514        let Some((serial, current, cancelled)) = self
515            .state
516            .conversations
517            .get(&key)
518            .and_then(|conversation| conversation.active.as_ref())
519            .map(|active| (active.serial, active.turn.clone(), active.cancelled))
520        else {
521            self.reject_if_tool(&kind, "no active turn").await?;
522            return Ok(());
523        };
524        let Some(current) = current else {
525            if cancelled && !matches!(&kind, ScopedKind::TurnCompleted(_) | ScopedKind::Error(_)) {
526                self.reject_if_tool(&kind, "turn was abandoned").await?;
527                return Ok(());
528            }
529            let active = self
530                .state
531                .conversations
532                .get_mut(&key)
533                .and_then(|conversation| conversation.active.as_mut())
534                .expect("active turn exists");
535            if active.early.len() >= EARLY_LIMIT {
536                self.reject_if_tool(&kind, "early event limit exceeded")
537                    .await?;
538                return Err(self.error(ErrorKind::Protocol, "early event limit exceeded"));
539            }
540            active.early.push(message);
541            return Ok(());
542        };
543        if current != scope.turn_id {
544            self.reject_if_tool(&kind, "request is outside the active turn")
545                .await?;
546            return Ok(());
547        }
548        if cancelled && !matches!(&kind, ScopedKind::TurnCompleted(_)) {
549            self.reject_if_tool(&kind, "turn was abandoned").await?;
550            return Ok(());
551        }
552        self.dispatch(key, serial, kind).await
553    }
554    async fn dispatch(&mut self, key: String, serial: u64, kind: ScopedKind) -> Result<(), Error> {
555        match kind {
556            ScopedKind::AgentTextDelta(delta) => {
557                if !self.emit(&key, serial, Event::TextDelta(delta)) {
558                    self.abandon(&key, serial).await?;
559                }
560            }
561            ScopedKind::DynamicToolCall(call) => self.tool_call(&key, serial, call).await?,
562            ScopedKind::TurnCompleted(completed) => self.complete(&key, serial, completed).await?,
563            ScopedKind::Error(error) => {
564                if let Some(active) = self
565                    .state
566                    .conversations
567                    .get_mut(&key)
568                    .and_then(|conversation| conversation.active.as_mut())
569                    .filter(|active| active.serial == serial)
570                {
571                    active.failure = Some(error.details);
572                }
573            }
574            ScopedKind::TurnStarted => {}
575        }
576        Ok(())
577    }
578    async fn tool_call(
579        &mut self,
580        key: &str,
581        serial: u64,
582        call: DecodedToolCall,
583    ) -> Result<(), Error> {
584        if !self.config.tools.iter().any(|tool| tool.name == call.name) {
585            self.reject(&call.rpc_id, -32602, "unconfigured dynamic tool")
586                .await?;
587            return self
588                .fail_turn(
589                    key,
590                    serial,
591                    self.error(ErrorKind::Protocol, "unconfigured dynamic tool requested"),
592                )
593                .await;
594        }
595        let id = call.rpc_id.as_value();
596        if let Err(error) = self
597            .state
598            .track_tool(key, serial, call.call_id.clone(), &id)
599        {
600            let error = self.stamp(error);
601            if error.message != "duplicate dynamic tool call id" {
602                return Err(error);
603            }
604            self.reject(&call.rpc_id, -32602, &error.message).await?;
605            return self.fail_turn(key, serial, error).await;
606        }
607        if !self.emit(
608            key,
609            serial,
610            Event::ToolCall(ToolCall {
611                call_id: call.call_id,
612                name: call.name,
613                arguments: call.arguments,
614            }),
615        ) {
616            self.abandon(key, serial).await?;
617        }
618        Ok(())
619    }
620    async fn complete(
621        &mut self,
622        key: &str,
623        serial: u64,
624        completed: TurnCompleted,
625    ) -> Result<(), Error> {
626        let pending = self
627            .state
628            .tools
629            .keys()
630            .filter(|(owner, turn, _)| owner == key && *turn == serial)
631            .count();
632        if pending != 0 {
633            self.reject_tools(key, serial, "turn completed before tool results")
634                .await?;
635        }
636        let Some(mut active) = self.state.take_active(key, serial) else {
637            return Ok(());
638        };
639        if active.cancelled {
640            return Ok(());
641        }
642        let failure = completed
643            .failure
644            .or_else(|| active.failure.take().map(ServerError::new));
645        let event = match completed.status {
646            TurnStatus::Completed if pending == 0 => Event::Done,
647            TurnStatus::Completed => Event::Error(self.error(
648                ErrorKind::Protocol,
649                "turn completed with pending tool calls",
650            )),
651            TurnStatus::Interrupted => {
652                Event::Error(self.error(ErrorKind::Interrupted, "Codex turn was interrupted"))
653            }
654            TurnStatus::Failed => Event::Error(
655                failure
656                    .unwrap_or_else(|| ServerError::new(Value::Null))
657                    .to_error(self.transport.diagnostics()),
658            ),
659        };
660        if let Some(events) = active.sink {
661            let _ = events.send(event);
662        }
663        Ok(())
664    }
665    async fn prestart_fail(&mut self, key: &str, serial: u64, error: Error) -> Result<(), Error> {
666        if let Some(active) = self.state.take_active(key, serial) {
667            if let Some(events) = active.sink {
668                let _ = events.send(Event::Error(error));
669            }
670            self.reject_early(active.early, "turn failed to start")
671                .await?;
672        }
673        Ok(())
674    }
675    async fn fail_turn(&mut self, key: &str, serial: u64, error: Error) -> Result<(), Error> {
676        if let Some(active) = self
677            .state
678            .conversations
679            .get_mut(key)
680            .and_then(|conversation| conversation.active.as_mut())
681            .filter(|active| active.serial == serial)
682        {
683            if let Some(events) = active.sink.take() {
684                let _ = events.send(Event::Error(error));
685            }
686            active.cancelled = true;
687        }
688        self.reject_tools(key, serial, "turn failed").await?;
689        self.interrupt(key, serial).await
690    }
691    async fn abandon(&mut self, key: &str, serial: u64) -> Result<(), Error> {
692        if let Some(active) = self
693            .state
694            .conversations
695            .get_mut(key)
696            .and_then(|conversation| conversation.active.as_mut())
697            .filter(|active| active.serial == serial)
698        {
699            active.sink.take();
700            active.cancelled = true;
701        }
702        self.reject_tools(key, serial, "turn was abandoned").await?;
703        self.interrupt(key, serial).await
704    }
705    async fn interrupt(&mut self, key: &str, serial: u64) -> Result<(), Error> {
706        let cancelled = self
707            .state
708            .conversations
709            .get(key)
710            .and_then(|conversation| conversation.active.as_ref())
711            .is_some_and(|active| active.serial == serial && active.cancelled);
712        if cancelled && let Some((thread, turn)) = self.state.interrupt_target(key, serial) {
713            self.request(
714                "turn/interrupt",
715                json!({"threadId": thread, "turnId": turn}),
716                Pending::Interrupt,
717            )
718            .await?;
719        }
720        Ok(())
721    }
722    async fn retire_turn(&mut self, key: &str, turn: &str) -> Result<(), Error> {
723        let thread = self
724            .state
725            .thread(key)
726            .ok_or_else(|| {
727                self.error(
728                    ErrorKind::Protocol,
729                    "orphan turn had no conversation thread",
730                )
731            })?
732            .to_owned();
733        self.request(
734            "turn/interrupt",
735            json!({"threadId": thread, "turnId": turn}),
736            Pending::Interrupt,
737        )
738        .await?;
739        self.retire_thread(key).await
740    }
741    async fn retire_thread(&mut self, key: &str) -> Result<(), Error> {
742        let thread = match self.state.begin_close(key) {
743            Ok(Some(thread)) => thread,
744            Ok(None) => return Ok(()),
745            Err(error) => return Err(self.stamp(error)),
746        };
747        self.request(
748            "thread/unsubscribe",
749            json!({"threadId": thread}),
750            Pending::Close {
751                key: key.to_owned(),
752                thread,
753                reply: None,
754            },
755        )
756        .await
757    }
758    async fn reject_early(&mut self, early: Vec<Value>, reason: &str) -> Result<(), Error> {
759        for message in early {
760            match decode(message) {
761                Inbound::Scoped(event) => self.reject_if_tool(&event.kind, reason).await?,
762                Inbound::Malformed(error) => return Err(self.stamp(error)),
763                _ => {
764                    return Err(
765                        self.error(ErrorKind::Protocol, "buffered event changed classification")
766                    );
767                }
768            }
769        }
770        Ok(())
771    }
772    async fn reject_tools(&mut self, key: &str, serial: u64, reason: &str) -> Result<(), Error> {
773        for (_, pending) in self.state.take_turn_tools(key, serial) {
774            let id = RpcId::try_from(&pending.id).map_err(|error| self.stamp(error))?;
775            self.reject(&id, -32800, reason).await?;
776        }
777        Ok(())
778    }
779    async fn reject_all(&mut self, reason: &str) -> Result<(), Error> {
780        let tokens: Vec<_> = self.state.tools.keys().cloned().collect();
781        for token in tokens {
782            if let Some(pending) = self.state.take_tool(&token) {
783                let id = RpcId::try_from(&pending.id).map_err(|error| self.stamp(error))?;
784                self.reject(&id, -32800, reason).await?;
785            }
786        }
787        Ok(())
788    }
789    async fn reject_if_tool(&mut self, kind: &ScopedKind, reason: &str) -> Result<(), Error> {
790        if let ScopedKind::DynamicToolCall(call) = kind {
791            self.reject(&call.rpc_id, -32602, reason).await?;
792        }
793        Ok(())
794    }
795    async fn reject(&mut self, id: &RpcId, code: i64, reason: &str) -> Result<(), Error> {
796        let (_, key) = parse_rpc_id(&id.as_value()).map_err(|error| self.stamp(error))?;
797        if self.state.rpc_ids.contains(&key) {
798            return Err(self.error(ErrorKind::Protocol, "duplicate app-server request id"));
799        }
800        self.transport
801            .send(&rejection_payload(id, code, reason))
802            .await
803            .map_err(map_wire)
804    }
805    async fn request(
806        &mut self,
807        method: &str,
808        params: Value,
809        pending: Pending<CloseReply>,
810    ) -> Result<(), Error> {
811        let id = self
812            .state
813            .allocate_request_id()
814            .map_err(|error| self.stamp(error))?;
815        self.transport
816            .send(&json!({"method": method, "id": id, "params": params}))
817            .await
818            .map_err(map_wire)?;
819        self.state
820            .insert_pending(id, pending)
821            .map_err(|error| self.stamp(error))
822    }
823    fn start_pending(&self, key: &str) -> bool {
824        self.state.pending.values().any(|pending| matches!(pending, Pending::Thread { key: owner, .. } | Pending::Turn { key: owner, .. } if owner == key))
825    }
826    fn emit(&mut self, key: &str, serial: u64, event: Event) -> bool {
827        self.state
828            .conversations
829            .get_mut(key)
830            .and_then(|conversation| conversation.active.as_mut())
831            .filter(|active| active.serial == serial)
832            .and_then(|active| active.sink.as_ref())
833            .is_some_and(|events| events.send(event).is_ok())
834    }
835    fn error(&self, kind: ErrorKind, message: impl Into<String>) -> Error {
836        Error {
837            kind,
838            message: message.into(),
839            diagnostics: self.transport.diagnostics(),
840        }
841    }
842    fn stamp(&self, mut error: Error) -> Error {
843        error.diagnostics = self.transport.diagnostics();
844        error
845    }
846    fn refresh(&self) {
847        self.diagnostics.replace(self.transport.diagnostics());
848    }
849}
850fn map_wire(error: WireError) -> Error {
851    let kind = match error.kind {
852        WireKind::Protocol => ErrorKind::Protocol,
853        WireKind::Server => ErrorKind::Server,
854        WireKind::Spawn | WireKind::Timeout | WireKind::Io | WireKind::Closed => {
855            ErrorKind::Unavailable
856        }
857    };
858    Error {
859        kind,
860        message: error.message,
861        diagnostics: error.diagnostics,
862    }
863}