use agent_settings::{AgentSettings, SUMMARIZE_THREAD_PROMPT};use anyhow::{Context as _, Result, bail};use assistant_slash_command::{ SlashCommandContent, SlashCommandEvent, SlashCommandLine, SlashCommandOutputSection, SlashCommandResult, SlashCommandWorkingSet,};use client::{self, proto};use clock::ReplicaId;use cloud_llm_client::CompletionIntent;use collections::{HashMap, HashSet};use fs::{Fs, RenameOptions}; use futures::{FutureExt, StreamExt, future::Shared};use gpui::{ App, AppContext as _, Context, Entity, EventEmitter, RenderImage, SharedString, Subscription, Task,};use itertools::Itertools as _;use language::{AnchorRangeExt, Bias, Buffer, LanguageRegistry, OffsetRangeExt, Point, ToOffset};use language_model::{ AnthropicCompletionType, AnthropicEventData, AnthropicEventType, LanguageModel, LanguageModelCacheConfiguration, LanguageModelCompletionEvent, LanguageModelImage, LanguageModelRegistry, LanguageModelRequest, LanguageModelRequestMessage, LanguageModelToolUseId, MessageContent, PaymentRequiredError, Role, StopReason, report_anthropic_event,};use open_ai::Model as OpenAiModel;use paths::text_threads_dir;use prompt_store::PromptBuilder;use serde::{Deserialize, Serialize};use smallvec::SmallVec;use std::{ cmp::{Ordering, max}, fmt::{Debug, Write as _}, iter, mem, ops::Range, path::Path, sync::Arc, time::{Duration, Instant},}; use text::{BufferSnapshot, ToPoint};use ui::IconName;use util::{ResultExt, TryFutureExt, post_inc};use uuid::Uuid; #[derive(Clone, Debug, Eq, PartialEq, Hash, PartialOrd, Ord, Serialize, Deserialize)]pub struct TextThreadId(String); impl TextThreadId { pub fn new() -> Self { Self(Uuid::new_v4().to_string()) } pub fn from_proto(id: String) -> Self { Self(id) } pub fn to_proto(&self) -> String { self.0.clone() }} #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Hash, Serialize, Deserialize)]pub struct MessageId(pub clock::Lamport); impl MessageId { pub fn as_u64(self) -> u64 { self.0.as_u64() }} #[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]pub enum MessageStatus { Pending, Done, Error(SharedString), Canceled,} impl MessageStatus { pub fn from_proto(status: proto::ContextMessageStatus) -> MessageStatus { match status.variant { Some(proto::context_message_status::Variant::Pending(_)) => MessageStatus::Pending, Some(proto::context_message_status::Variant::Done(_)) => MessageStatus::Done, Some(proto::context_message_status::Variant::Error(error)) => { MessageStatus::Error(error.message.into()) } Some(proto::context_message_status::Variant::Canceled(_)) => MessageStatus::Canceled, None => MessageStatus::Pending, } } pub fn to_proto(&self) -> proto::ContextMessageStatus { match self { MessageStatus::Pending => proto::ContextMessageStatus { variant: Some(proto::context_message_status::Variant::Pending( proto::context_message_status::Pending {}, )), }, MessageStatus::Done => proto::ContextMessageStatus { variant: Some(proto::context_message_status::Variant::Done( proto::context_message_status::Done {}, )), }, MessageStatus::Error(message) => proto::ContextMessageStatus { variant: Some(proto::context_message_status::Variant::Error( proto::context_message_status::Error { message: message.to_string(), }, )), }, MessageStatus::Canceled => proto::ContextMessageStatus { variant: Some(proto::context_message_status::Variant::Canceled( proto::context_message_status::Canceled {}, )), }, } }} #[derive(Clone, Debug)]pub enum TextThreadOperation { InsertMessage { anchor: MessageAnchor, metadata: MessageMetadata, version: clock::Global, }, UpdateMessage { message_id: MessageId, metadata: MessageMetadata, version: clock::Global, }, UpdateSummary { summary: TextThreadSummaryContent, version: clock::Global, }, SlashCommandStarted { id: InvokedSlashCommandId, output_range: Range<language::Anchor>, name: String, version: clock::Global, }, SlashCommandFinished { id: InvokedSlashCommandId, timestamp: clock::Lamport, error_message: Option<String>, version: clock::Global, }, SlashCommandOutputSectionAdded { timestamp: clock::Lamport, section: SlashCommandOutputSection<language::Anchor>, version: clock::Global, }, ThoughtProcessOutputSectionAdded { timestamp: clock::Lamport, section: ThoughtProcessOutputSection<language::Anchor>, version: clock::Global, }, BufferOperation(language::Operation),} impl TextThreadOperation { pub fn from_proto(op: proto::ContextOperation) -> Result<Self> { match op.variant.context("invalid variant")? { proto::context_operation::Variant::InsertMessage(insert) => { let message = insert.message.context("invalid message")?; let id = MessageId(language::proto::deserialize_timestamp( message.id.context("invalid id")?, )); Ok(Self::InsertMessage { anchor: MessageAnchor { id, start: language::proto::deserialize_anchor( message.start.context("invalid anchor")?, ) .context("invalid anchor")?, }, metadata: MessageMetadata { role: Role::from_proto(message.role), status: MessageStatus::from_proto( message.status.context("invalid status")?, ), timestamp: id.0, cache: None, }, version: language::proto::deserialize_version(&insert.version), }) } proto::context_operation::Variant::UpdateMessage(update) => Ok(Self::UpdateMessage { message_id: MessageId(language::proto::deserialize_timestamp( update.message_id.context("invalid message id")?, )), metadata: MessageMetadata { role: Role::from_proto(update.role), status: MessageStatus::from_proto(update.status.context("invalid status")?), timestamp: language::proto::deserialize_timestamp( update.timestamp.context("invalid timestamp")?, ), cache: None, }, version: language::proto::deserialize_version(&update.version), }), proto::context_operation::Variant::UpdateSummary(update) => Ok(Self::UpdateSummary { summary: TextThreadSummaryContent { text: update.summary, done: update.done, timestamp: language::proto::deserialize_timestamp( update.timestamp.context("invalid timestamp")?, ), }, version: language::proto::deserialize_version(&update.version), }), proto::context_operation::Variant::SlashCommandStarted(message) => { Ok(Self::SlashCommandStarted { id: InvokedSlashCommandId(language::proto::deserialize_timestamp( message.id.context("invalid id")?, )), output_range: language::proto::deserialize_anchor_range( message.output_range.context("invalid range")?, )?, name: message.name, version: language::proto::deserialize_version(&message.version), }) } proto::context_operation::Variant::SlashCommandOutputSectionAdded(message) => { let section = message.section.context("missing section")?; Ok(Self::SlashCommandOutputSectionAdded { timestamp: language::proto::deserialize_timestamp( message.timestamp.context("missing timestamp")?, ), section: SlashCommandOutputSection { range: language::proto::deserialize_anchor_range( section.range.context("invalid range")?, )?, icon: section.icon_name.parse()?, label: section.label.into(), metadata: section .metadata .and_then(|metadata| serde_json::from_str(&metadata).log_err()), }, version: language::proto::deserialize_version(&message.version), }) } proto::context_operation::Variant::SlashCommandCompleted(message) => { Ok(Self::SlashCommandFinished { id: InvokedSlashCommandId(language::proto::deserialize_timestamp( message.id.context("invalid id")?, )), timestamp: language::proto::deserialize_timestamp( message.timestamp.context("missing timestamp")?, ), error_message: message.error_message, version: language::proto::deserialize_version(&message.version), }) } proto::context_operation::Variant::ThoughtProcessOutputSectionAdded(message) => { let section = message.section.context("missing section")?; Ok(Self::ThoughtProcessOutputSectionAdded { timestamp: language::proto::deserialize_timestamp( message.timestamp.context("missing timestamp")?, ), section: ThoughtProcessOutputSection { range: language::proto::deserialize_anchor_range( section.range.context("invalid range")?, )?, }, version: language::proto::deserialize_version(&message.version), }) } proto::context_operation::Variant::BufferOperation(op) => Ok(Self::BufferOperation( language::proto::deserialize_operation( op.operation.context("invalid buffer operation")?, )?, )), } } pub fn to_proto(&self) -> proto::ContextOperation { match self { Self::InsertMessage { anchor, metadata, version, } => proto::ContextOperation { variant: Some(proto::context_operation::Variant::InsertMessage( proto::context_operation::InsertMessage { message: Some(proto::ContextMessage { id: Some(language::proto::serialize_timestamp(anchor.id.0)), start: Some(language::proto::serialize_anchor(&anchor.start)), role: metadata.role.to_proto() as i32, status: Some(metadata.status.to_proto()), }), version: language::proto::serialize_version(version), }, )), }, Self::UpdateMessage { message_id, metadata, version, } => proto::ContextOperation { variant: Some(proto::context_operation::Variant::UpdateMessage( proto::context_operation::UpdateMessage { message_id: Some(language::proto::serialize_timestamp(message_id.0)), role: metadata.role.to_proto() as i32, status: Some(metadata.status.to_proto()), timestamp: Some(language::proto::serialize_timestamp(metadata.timestamp)), version: language::proto::serialize_version(version), }, )), }, Self::UpdateSummary { summary, version } => proto::ContextOperation { variant: Some(proto::context_operation::Variant::UpdateSummary( proto::context_operation::UpdateSummary { summary: summary.text.clone(), done: summary.done, timestamp: Some(language::proto::serialize_timestamp(summary.timestamp)), version: language::proto::serialize_version(version), }, )), }, Self::SlashCommandStarted { id, output_range, name, version, } => proto::ContextOperation { variant: Some(proto::context_operation::Variant::SlashCommandStarted( proto::context_operation::SlashCommandStarted { id: Some(language::proto::serialize_timestamp(id.0)), output_range: Some(language::proto::serialize_anchor_range( output_range.clone(), )), name: name.clone(), version: language::proto::serialize_version(version), }, )), }, Self::SlashCommandOutputSectionAdded { timestamp, section, version, } => proto::ContextOperation { variant: Some( proto::context_operation::Variant::SlashCommandOutputSectionAdded( proto::context_operation::SlashCommandOutputSectionAdded { timestamp: Some(language::proto::serialize_timestamp(*timestamp)), section: Some({ let icon_name: &'static str = section.icon.into(); proto::SlashCommandOutputSection { range: Some(language::proto::serialize_anchor_range( section.range.clone(), )), icon_name: icon_name.to_string(), label: section.label.to_string(), metadata: section.metadata.as_ref().and_then(|metadata| { serde_json::to_string(metadata).log_err() }), } }), version: language::proto::serialize_version(version), }, ), ), }, Self::SlashCommandFinished { id, timestamp, error_message, version, } => proto::ContextOperation { variant: Some(proto::context_operation::Variant::SlashCommandCompleted( proto::context_operation::SlashCommandCompleted { id: Some(language::proto::serialize_timestamp(id.0)), timestamp: Some(language::proto::serialize_timestamp(*timestamp)), error_message: error_message.clone(), version: language::proto::serialize_version(version), }, )), }, Self::ThoughtProcessOutputSectionAdded { timestamp, section, version, } => proto::ContextOperation { variant: Some( proto::context_operation::Variant::ThoughtProcessOutputSectionAdded( proto::context_operation::ThoughtProcessOutputSectionAdded { timestamp: Some(language::proto::serialize_timestamp(*timestamp)), section: Some({ proto::ThoughtProcessOutputSection { range: Some(language::proto::serialize_anchor_range( section.range.clone(), )), } }), version: language::proto::serialize_version(version), }, ), ), }, Self::BufferOperation(operation) => proto::ContextOperation { variant: Some(proto::context_operation::Variant::BufferOperation( proto::context_operation::BufferOperation { operation: Some(language::proto::serialize_operation(operation)), }, )), }, } } fn timestamp(&self) -> clock::Lamport { match self { Self::InsertMessage { anchor, .. } => anchor.id.0, Self::UpdateMessage { metadata, .. } => metadata.timestamp, Self::UpdateSummary { summary, .. } => summary.timestamp, Self::SlashCommandStarted { id, .. } => id.0, Self::SlashCommandOutputSectionAdded { timestamp, .. } | Self::SlashCommandFinished { timestamp, .. } | Self::ThoughtProcessOutputSectionAdded { timestamp, .. } => *timestamp, Self::BufferOperation(_) => { panic!("reading the timestamp of a buffer operation is not supported") } } } /// Returns the current version of the context operation. pub fn version(&self) -> &clock::Global { match self { Self::InsertMessage { version, .. } | Self::UpdateMessage { version, .. } | Self::UpdateSummary { version, .. } | Self::SlashCommandStarted { version, .. } | Self::SlashCommandOutputSectionAdded { version, .. } | Self::SlashCommandFinished { version, .. } | Self::ThoughtProcessOutputSectionAdded { version, .. } => version, Self::BufferOperation(_) => { panic!("reading the version of a buffer operation is not supported") } } }} #[derive(Debug, Clone)]pub enum TextThreadEvent { ShowAssistError(SharedString), ShowPaymentRequiredError, MessagesEdited, SummaryChanged, SummaryGenerated, PathChanged { old_path: Option<Arc<Path>>, new_path: Arc<Path>, }, StreamedCompletion, StartedThoughtProcess(Range<language::Anchor>), EndedThoughtProcess(language::Anchor), InvokedSlashCommandChanged { command_id: InvokedSlashCommandId, }, ParsedSlashCommandsUpdated { removed: Vec<Range<language::Anchor>>, updated: Vec<ParsedSlashCommand>, }, SlashCommandOutputSectionAdded { section: SlashCommandOutputSection<language::Anchor>, }, Operation(TextThreadOperation),} #[derive(Clone, Debug, Eq, PartialEq)]pub enum TextThreadSummary { Pending, Content(TextThreadSummaryContent), Error,} #[derive(Clone, Debug, Eq, PartialEq)]pub struct TextThreadSummaryContent { pub text: String, pub done: bool, pub timestamp: clock::Lamport,} impl TextThreadSummary { pub const DEFAULT: &str = "New Text Thread"; pub fn or_default(&self) -> SharedString { self.unwrap_or(Self::DEFAULT) } pub fn unwrap_or(&self, message: impl Into<SharedString>) -> SharedString { self.content() .map_or_else(|| message.into(), |content| content.text.clone().into()) } pub fn content(&self) -> Option<&TextThreadSummaryContent> { match self { TextThreadSummary::Content(content) => Some(content),Lines 1–500 of 3287. Load more
use agent_settings::{AgentSettings, SUMMARIZE_THREAD_PROMPT}; use anyhow::{Context as _, Result, bail}; use assistant_slash_command::{ SlashCommandContent, SlashCommandEvent, SlashCommandLine, SlashCommandOutputSection, SlashCommandResult, SlashCommandWorkingSet, }; use client::{self, proto}; use clock::ReplicaId; use cloud_llm_client::CompletionIntent; use collections::{HashMap, HashSet}; use fs::{Fs, RenameOptions}; use futures::{FutureExt, StreamExt, future::Shared}; use gpui::{ App, AppContext as _, Context, Entity, EventEmitter, RenderImage, SharedString, Subscription, Task, }; use itertools::Itertools as _; use language::{AnchorRangeExt, Bias, Buffer, LanguageRegistry, OffsetRangeExt, Point, ToOffset}; use language_model::{ AnthropicCompletionType, AnthropicEventData, AnthropicEventType, LanguageModel, LanguageModelCacheConfiguration, LanguageModelCompletionEvent, LanguageModelImage, LanguageModelRegistry, LanguageModelRequest, LanguageModelRequestMessage, LanguageModelToolUseId, MessageContent, PaymentRequiredError, Role, StopReason, report_anthropic_event, }; use open_ai::Model as OpenAiModel; use paths::text_threads_dir; use prompt_store::PromptBuilder; use serde::{Deserialize, Serialize}; use smallvec::SmallVec; use std::{ cmp::{Ordering, max}, fmt::{Debug, Write as _}, iter, mem, ops::Range, path::Path, sync::Arc, time::{Duration, Instant}, }; use text::{BufferSnapshot, ToPoint}; use ui::IconName; use util::{ResultExt, TryFutureExt, post_inc}; use uuid::Uuid; #[derive(Clone, Debug, Eq, PartialEq, Hash, PartialOrd, Ord, Serialize, Deserialize)] pub struct TextThreadId(String); impl TextThreadId { pub fn new() -> Self { Self(Uuid::new_v4().to_string()) } pub fn from_proto(id: String) -> Self { Self(id) } pub fn to_proto(&self) -> String { self.0.clone() } } #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Hash, Serialize, Deserialize)] pub struct MessageId(pub clock::Lamport); impl MessageId { pub fn as_u64(self) -> u64 { self.0.as_u64() } } #[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] pub enum MessageStatus { Pending, Done, Error(SharedString), Canceled, } impl MessageStatus { pub fn from_proto(status: proto::ContextMessageStatus) -> MessageStatus { match status.variant { Some(proto::context_message_status::Variant::Pending(_)) => MessageStatus::Pending, Some(proto::context_message_status::Variant::Done(_)) => MessageStatus::Done, Some(proto::context_message_status::Variant::Error(error)) => { MessageStatus::Error(error.message.into()) } Some(proto::context_message_status::Variant::Canceled(_)) => MessageStatus::Canceled, None => MessageStatus::Pending, } } pub fn to_proto(&self) -> proto::ContextMessageStatus { match self { MessageStatus::Pending => proto::ContextMessageStatus { variant: Some(proto::context_message_status::Variant::Pending( proto::context_message_status::Pending {}, )), }, MessageStatus::Done => proto::ContextMessageStatus { variant: Some(proto::context_message_status::Variant::Done( proto::context_message_status::Done {}, )), }, MessageStatus::Error(message) => proto::ContextMessageStatus { variant: Some(proto::context_message_status::Variant::Error( proto::context_message_status::Error { message: message.to_string(), }, )), }, MessageStatus::Canceled => proto::ContextMessageStatus { variant: Some(proto::context_message_status::Variant::Canceled( proto::context_message_status::Canceled {}, )), }, } } } #[derive(Clone, Debug)] pub enum TextThreadOperation { InsertMessage { anchor: MessageAnchor, metadata: MessageMetadata, version: clock::Global, }, UpdateMessage { message_id: MessageId, metadata: MessageMetadata, version: clock::Global, }, UpdateSummary { summary: TextThreadSummaryContent, version: clock::Global, }, SlashCommandStarted { id: InvokedSlashCommandId, output_range: Range<language::Anchor>, name: String, version: clock::Global, }, SlashCommandFinished { id: InvokedSlashCommandId, timestamp: clock::Lamport, error_message: Option<String>, version: clock::Global, }, SlashCommandOutputSectionAdded { timestamp: clock::Lamport, section: SlashCommandOutputSection<language::Anchor>, version: clock::Global, }, ThoughtProcessOutputSectionAdded { timestamp: clock::Lamport, section: ThoughtProcessOutputSection<language::Anchor>, version: clock::Global, }, BufferOperation(language::Operation), } impl TextThreadOperation { pub fn from_proto(op: proto::ContextOperation) -> Result<Self> { match op.variant.context("invalid variant")? { proto::context_operation::Variant::InsertMessage(insert) => { let message = insert.message.context("invalid message")?; let id = MessageId(language::proto::deserialize_timestamp( message.id.context("invalid id")?, )); Ok(Self::InsertMessage { anchor: MessageAnchor { id, start: language::proto::deserialize_anchor( message.start.context("invalid anchor")?, ) .context("invalid anchor")?, }, metadata: MessageMetadata { role: Role::from_proto(message.role), status: MessageStatus::from_proto( message.status.context("invalid status")?, ), timestamp: id.0, cache: None, }, version: language::proto::deserialize_version(&insert.version), }) } proto::context_operation::Variant::UpdateMessage(update) => Ok(Self::UpdateMessage { message_id: MessageId(language::proto::deserialize_timestamp( update.message_id.context("invalid message id")?, )), metadata: MessageMetadata { role: Role::from_proto(update.role), status: MessageStatus::from_proto(update.status.context("invalid status")?), timestamp: language::proto::deserialize_timestamp( update.timestamp.context("invalid timestamp")?, ), cache: None, }, version: language::proto::deserialize_version(&update.version), }), proto::context_operation::Variant::UpdateSummary(update) => Ok(Self::UpdateSummary { summary: TextThreadSummaryContent { text: update.summary, done: update.done, timestamp: language::proto::deserialize_timestamp( update.timestamp.context("invalid timestamp")?, ), }, version: language::proto::deserialize_version(&update.version), }), proto::context_operation::Variant::SlashCommandStarted(message) => { Ok(Self::SlashCommandStarted { id: InvokedSlashCommandId(language::proto::deserialize_timestamp( message.id.context("invalid id")?, )), output_range: language::proto::deserialize_anchor_range( message.output_range.context("invalid range")?, )?, name: message.name, version: language::proto::deserialize_version(&message.version), }) } proto::context_operation::Variant::SlashCommandOutputSectionAdded(message) => { let section = message.section.context("missing section")?; Ok(Self::SlashCommandOutputSectionAdded { timestamp: language::proto::deserialize_timestamp( message.timestamp.context("missing timestamp")?, ), section: SlashCommandOutputSection { range: language::proto::deserialize_anchor_range( section.range.context("invalid range")?, )?, icon: section.icon_name.parse()?, label: section.label.into(), metadata: section .metadata .and_then(|metadata| serde_json::from_str(&metadata).log_err()), }, version: language::proto::deserialize_version(&message.version), }) } proto::context_operation::Variant::SlashCommandCompleted(message) => { Ok(Self::SlashCommandFinished { id: InvokedSlashCommandId(language::proto::deserialize_timestamp( message.id.context("invalid id")?, )), timestamp: language::proto::deserialize_timestamp( message.timestamp.context("missing timestamp")?, ), error_message: message.error_message, version: language::proto::deserialize_version(&message.version), }) } proto::context_operation::Variant::ThoughtProcessOutputSectionAdded(message) => { let section = message.section.context("missing section")?; Ok(Self::ThoughtProcessOutputSectionAdded { timestamp: language::proto::deserialize_timestamp( message.timestamp.context("missing timestamp")?, ), section: ThoughtProcessOutputSection { range: language::proto::deserialize_anchor_range( section.range.context("invalid range")?, )?, }, version: language::proto::deserialize_version(&message.version), }) } proto::context_operation::Variant::BufferOperation(op) => Ok(Self::BufferOperation( language::proto::deserialize_operation( op.operation.context("invalid buffer operation")?, )?, )), } } pub fn to_proto(&self) -> proto::ContextOperation { match self { Self::InsertMessage { anchor, metadata, version, } => proto::ContextOperation { variant: Some(proto::context_operation::Variant::InsertMessage( proto::context_operation::InsertMessage { message: Some(proto::ContextMessage { id: Some(language::proto::serialize_timestamp(anchor.id.0)), start: Some(language::proto::serialize_anchor(&anchor.start)), role: metadata.role.to_proto() as i32, status: Some(metadata.status.to_proto()), }), version: language::proto::serialize_version(version), }, )), }, Self::UpdateMessage { message_id, metadata, version, } => proto::ContextOperation { variant: Some(proto::context_operation::Variant::UpdateMessage( proto::context_operation::UpdateMessage { message_id: Some(language::proto::serialize_timestamp(message_id.0)), role: metadata.role.to_proto() as i32, status: Some(metadata.status.to_proto()), timestamp: Some(language::proto::serialize_timestamp(metadata.timestamp)), version: language::proto::serialize_version(version), }, )), }, Self::UpdateSummary { summary, version } => proto::ContextOperation { variant: Some(proto::context_operation::Variant::UpdateSummary( proto::context_operation::UpdateSummary { summary: summary.text.clone(), done: summary.done, timestamp: Some(language::proto::serialize_timestamp(summary.timestamp)), version: language::proto::serialize_version(version), }, )), }, Self::SlashCommandStarted { id, output_range, name, version, } => proto::ContextOperation { variant: Some(proto::context_operation::Variant::SlashCommandStarted( proto::context_operation::SlashCommandStarted { id: Some(language::proto::serialize_timestamp(id.0)), output_range: Some(language::proto::serialize_anchor_range( output_range.clone(), )), name: name.clone(), version: language::proto::serialize_version(version), }, )), }, Self::SlashCommandOutputSectionAdded { timestamp, section, version, } => proto::ContextOperation { variant: Some( proto::context_operation::Variant::SlashCommandOutputSectionAdded( proto::context_operation::SlashCommandOutputSectionAdded { timestamp: Some(language::proto::serialize_timestamp(*timestamp)), section: Some({ let icon_name: &'static str = section.icon.into(); proto::SlashCommandOutputSection { range: Some(language::proto::serialize_anchor_range( section.range.clone(), )), icon_name: icon_name.to_string(), label: section.label.to_string(), metadata: section.metadata.as_ref().and_then(|metadata| { serde_json::to_string(metadata).log_err() }), } }), version: language::proto::serialize_version(version), }, ), ), }, Self::SlashCommandFinished { id, timestamp, error_message, version, } => proto::ContextOperation { variant: Some(proto::context_operation::Variant::SlashCommandCompleted( proto::context_operation::SlashCommandCompleted { id: Some(language::proto::serialize_timestamp(id.0)), timestamp: Some(language::proto::serialize_timestamp(*timestamp)), error_message: error_message.clone(), version: language::proto::serialize_version(version), }, )), }, Self::ThoughtProcessOutputSectionAdded { timestamp, section, version, } => proto::ContextOperation { variant: Some( proto::context_operation::Variant::ThoughtProcessOutputSectionAdded( proto::context_operation::ThoughtProcessOutputSectionAdded { timestamp: Some(language::proto::serialize_timestamp(*timestamp)), section: Some({ proto::ThoughtProcessOutputSection { range: Some(language::proto::serialize_anchor_range( section.range.clone(), )), } }), version: language::proto::serialize_version(version), }, ), ), }, Self::BufferOperation(operation) => proto::ContextOperation { variant: Some(proto::context_operation::Variant::BufferOperation( proto::context_operation::BufferOperation { operation: Some(language::proto::serialize_operation(operation)), }, )), }, } } fn timestamp(&self) -> clock::Lamport { match self { Self::InsertMessage { anchor, .. } => anchor.id.0, Self::UpdateMessage { metadata, .. } => metadata.timestamp, Self::UpdateSummary { summary, .. } => summary.timestamp, Self::SlashCommandStarted { id, .. } => id.0, Self::SlashCommandOutputSectionAdded { timestamp, .. } | Self::SlashCommandFinished { timestamp, .. } | Self::ThoughtProcessOutputSectionAdded { timestamp, .. } => *timestamp, Self::BufferOperation(_) => { panic!("reading the timestamp of a buffer operation is not supported") } } } /// Returns the current version of the context operation. pub fn version(&self) -> &clock::Global { match self { Self::InsertMessage { version, .. } | Self::UpdateMessage { version, .. } | Self::UpdateSummary { version, .. } | Self::SlashCommandStarted { version, .. } | Self::SlashCommandOutputSectionAdded { version, .. } | Self::SlashCommandFinished { version, .. } | Self::ThoughtProcessOutputSectionAdded { version, .. } => version, Self::BufferOperation(_) => { panic!("reading the version of a buffer operation is not supported") } } } } #[derive(Debug, Clone)] pub enum TextThreadEvent { ShowAssistError(SharedString), ShowPaymentRequiredError, MessagesEdited, SummaryChanged, SummaryGenerated, PathChanged { old_path: Option<Arc<Path>>, new_path: Arc<Path>, }, StreamedCompletion, StartedThoughtProcess(Range<language::Anchor>), EndedThoughtProcess(language::Anchor), InvokedSlashCommandChanged { command_id: InvokedSlashCommandId, }, ParsedSlashCommandsUpdated { removed: Vec<Range<language::Anchor>>, updated: Vec<ParsedSlashCommand>, }, SlashCommandOutputSectionAdded { section: SlashCommandOutputSection<language::Anchor>, }, Operation(TextThreadOperation), } #[derive(Clone, Debug, Eq, PartialEq)] pub enum TextThreadSummary { Pending, Content(TextThreadSummaryContent), Error, } #[derive(Clone, Debug, Eq, PartialEq)] pub struct TextThreadSummaryContent { pub text: String, pub done: bool, pub timestamp: clock::Lamport, } impl TextThreadSummary { pub const DEFAULT: &str = "New Text Thread"; pub fn or_default(&self) -> SharedString { self.unwrap_or(Self::DEFAULT) } pub fn unwrap_or(&self, message: impl Into<SharedString>) -> SharedString { self.content() .map_or_else(|| message.into(), |content| content.text.clone().into()) } pub fn content(&self) -> Option<&TextThreadSummaryContent> { match self { TextThreadSummary::Content(content) => Some(content),