feat(desktop): add markdown format option to session export (#10703)
Co-authored-by: Douwe M Osinga <douwe@sidewalklabs.com>
This commit is contained in:
@@ -1,4 +1,3 @@
|
||||
use crate::session::user_projected_message_to_markdown;
|
||||
use anyhow::{Context, Result};
|
||||
|
||||
use cliclack::{confirm, multiselect, select};
|
||||
@@ -8,7 +7,8 @@ use goose::config::Config;
|
||||
#[cfg(feature = "nostr")]
|
||||
use goose::session::nostr_share;
|
||||
use goose::session::{
|
||||
generate_diagnostics, DiagnosticsLevel, Session, SessionManager, SessionType,
|
||||
export_session_to_markdown, generate_diagnostics, DiagnosticsLevel, Session, SessionManager,
|
||||
SessionType,
|
||||
};
|
||||
use goose::utils::safe_truncate;
|
||||
use regex::Regex;
|
||||
@@ -365,74 +365,6 @@ pub async fn handle_diagnostics(session_id: &str, output_path: Option<PathBuf>)
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn export_session_to_markdown(
|
||||
messages: Vec<goose::conversation::message::Message>,
|
||||
session_name: &String,
|
||||
) -> String {
|
||||
let mut markdown_output = String::new();
|
||||
|
||||
markdown_output.push_str(&format!("# Session Export: {}\n\n", session_name));
|
||||
|
||||
if messages.is_empty() {
|
||||
markdown_output.push_str("*(This session has no messages)*\n");
|
||||
return markdown_output;
|
||||
}
|
||||
|
||||
markdown_output.push_str(&format!("*Total messages: {}*\n\n---\n\n", messages.len()));
|
||||
|
||||
// Track if the last message had tool requests to properly handle tool responses
|
||||
let mut skip_next_if_tool_response = false;
|
||||
|
||||
for message in &messages {
|
||||
// Check if this is a User message containing only ToolResponses
|
||||
let is_only_tool_response = message.role == rmcp::model::Role::User
|
||||
&& message.content.iter().all(|content| {
|
||||
matches!(
|
||||
content,
|
||||
goose::conversation::message::MessageContent::ToolResponse(_)
|
||||
)
|
||||
});
|
||||
|
||||
// If the previous message had tool requests and this one is just tool responses,
|
||||
// don't create a new User section - we'll attach the responses to the tool calls
|
||||
if skip_next_if_tool_response && is_only_tool_response {
|
||||
// Export the tool responses without a User heading
|
||||
markdown_output.push_str(&user_projected_message_to_markdown(message));
|
||||
markdown_output.push_str("\n\n---\n\n");
|
||||
skip_next_if_tool_response = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Reset the skip flag - we'll update it below if needed
|
||||
skip_next_if_tool_response = false;
|
||||
|
||||
// Output the role prefix except for tool response-only messages
|
||||
if !is_only_tool_response {
|
||||
let role_prefix = match message.role {
|
||||
rmcp::model::Role::User => "### User:\n",
|
||||
rmcp::model::Role::Assistant => "### Assistant:\n",
|
||||
};
|
||||
markdown_output.push_str(role_prefix);
|
||||
}
|
||||
|
||||
// Add the message content
|
||||
markdown_output.push_str(&user_projected_message_to_markdown(message));
|
||||
markdown_output.push_str("\n\n---\n\n");
|
||||
|
||||
// Check if this message has any tool requests, to handle the next message differently
|
||||
if message.content.iter().any(|content| {
|
||||
matches!(
|
||||
content,
|
||||
goose::conversation::message::MessageContent::ToolRequest(_)
|
||||
)
|
||||
}) {
|
||||
skip_next_if_tool_response = true;
|
||||
}
|
||||
}
|
||||
|
||||
markdown_output
|
||||
}
|
||||
|
||||
/// Prompt the user to interactively select a session
|
||||
///
|
||||
/// Shows a list of available sessions and lets the user select one
|
||||
@@ -487,40 +419,3 @@ pub async fn prompt_interactive_session_selection(
|
||||
Err(anyhow::anyhow!("Invalid selection"))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use goose::conversation::message::Message;
|
||||
use goose::conversation::Conversation;
|
||||
use rmcp::model::{Annotations, ContentBlock, Role, TextContent};
|
||||
|
||||
#[test]
|
||||
fn markdown_export_preserves_user_audience_tool_output() {
|
||||
let user_output = ContentBlock::Text(
|
||||
TextContent::new("user-visible output")
|
||||
.with_annotations(Annotations::default().with_audience(vec![Role::User])),
|
||||
);
|
||||
let assistant_output = ContentBlock::Text(
|
||||
TextContent::new("assistant-only output")
|
||||
.with_annotations(Annotations::default().with_audience(vec![Role::Assistant])),
|
||||
);
|
||||
let conversation = Conversation::new_unvalidated([Message::user().with_tool_response(
|
||||
"tool-1",
|
||||
Ok(rmcp::model::CallToolResult::success(vec![
|
||||
user_output,
|
||||
assistant_output,
|
||||
ContentBlock::text("shared output"),
|
||||
])),
|
||||
)]);
|
||||
|
||||
let markdown = export_session_to_markdown(
|
||||
conversation.user_visible_messages(),
|
||||
&"Audience export".to_string(),
|
||||
);
|
||||
|
||||
assert!(markdown.contains("user-visible output"));
|
||||
assert!(markdown.contains("shared output"));
|
||||
assert!(!markdown.contains("assistant-only output"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ mod builder;
|
||||
mod completion;
|
||||
pub mod editor;
|
||||
mod elicitation;
|
||||
mod export;
|
||||
mod input;
|
||||
mod output;
|
||||
mod paste;
|
||||
@@ -20,7 +19,6 @@ use std::str::FromStr;
|
||||
use tokio::signal::ctrl_c;
|
||||
use tokio_util::task::AbortOnDropHandle;
|
||||
|
||||
pub use self::export::{message_to_markdown, user_projected_message_to_markdown};
|
||||
pub use builder::{build_session, SessionBuilderConfig};
|
||||
use console::Color;
|
||||
use goose::agents::platform_extensions::developer::shell::{
|
||||
|
||||
@@ -758,15 +758,26 @@ pub struct UnarchiveSessionRequest {
|
||||
pub session_id: String,
|
||||
}
|
||||
|
||||
/// Export a session as a JSON string.
|
||||
/// Export a session as a JSON or markdown string.
|
||||
#[derive(Debug, Default, Clone, Serialize, Deserialize, JsonSchema, JsonRpcRequest)]
|
||||
#[request(method = "_goose/unstable/session/export", response = ExportSessionResponse)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ExportSessionRequest {
|
||||
pub session_id: String,
|
||||
#[serde(default)]
|
||||
pub format: SessionExportFormat,
|
||||
}
|
||||
|
||||
/// Export session response — raw JSON of the goose session with `conversation`.
|
||||
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum SessionExportFormat {
|
||||
#[default]
|
||||
Json,
|
||||
Markdown,
|
||||
}
|
||||
|
||||
/// Export session response — raw JSON of the goose session with `conversation`,
|
||||
/// or a markdown transcript when `format` is `markdown`.
|
||||
#[derive(Debug, Default, Clone, Serialize, Deserialize, JsonSchema, JsonRpcResponse)]
|
||||
pub struct ExportSessionResponse {
|
||||
pub data: String,
|
||||
@@ -2262,3 +2273,23 @@ pub struct SetToolPermissionsRequest {
|
||||
|
||||
#[derive(Debug, Default, Clone, Serialize, Deserialize, JsonSchema, JsonRpcResponse)]
|
||||
pub struct SetToolPermissionsResponse {}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn export_session_request_defaults_to_json_without_format() {
|
||||
let req: ExportSessionRequest = serde_json::from_str(r#"{"sessionId":"abc"}"#).unwrap();
|
||||
|
||||
assert_eq!(req.format, SessionExportFormat::Json);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn export_session_request_accepts_markdown_format() {
|
||||
let req: ExportSessionRequest =
|
||||
serde_json::from_str(r#"{"sessionId":"abc","format":"markdown"}"#).unwrap();
|
||||
|
||||
assert_eq!(req.format, SessionExportFormat::Markdown);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3409,15 +3409,26 @@
|
||||
"properties": {
|
||||
"sessionId": {
|
||||
"type": "string"
|
||||
},
|
||||
"format": {
|
||||
"$ref": "#/$defs/SessionExportFormat",
|
||||
"default": "json"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"sessionId"
|
||||
],
|
||||
"description": "Export a session as a JSON string.",
|
||||
"description": "Export a session as a JSON or markdown string.",
|
||||
"x-side": "agent",
|
||||
"x-method": "_goose/unstable/session/export"
|
||||
},
|
||||
"SessionExportFormat": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"json",
|
||||
"markdown"
|
||||
]
|
||||
},
|
||||
"ExportSessionResponse_unstable": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -3428,7 +3439,7 @@
|
||||
"required": [
|
||||
"data"
|
||||
],
|
||||
"description": "Export session response — raw JSON of the goose session with `conversation`.",
|
||||
"description": "Export session response — raw JSON of the goose session with `conversation`,\nor a markdown transcript when `format` is `markdown`.",
|
||||
"x-side": "agent",
|
||||
"x-method": "_goose/unstable/session/export"
|
||||
},
|
||||
|
||||
@@ -115,11 +115,15 @@ impl GooseAcpAgent {
|
||||
&self,
|
||||
req: ExportSessionRequest,
|
||||
) -> Result<ExportSessionResponse, agent_client_protocol::Error> {
|
||||
let data = self
|
||||
.session_manager
|
||||
.export_session(&req.session_id)
|
||||
.await
|
||||
.internal_err()?;
|
||||
let data = match req.format {
|
||||
SessionExportFormat::Json => self.session_manager.export_session(&req.session_id).await,
|
||||
SessionExportFormat::Markdown => {
|
||||
self.session_manager
|
||||
.export_session_markdown(&req.session_id)
|
||||
.await
|
||||
}
|
||||
}
|
||||
.internal_err()?;
|
||||
Ok(ExportSessionResponse { data })
|
||||
}
|
||||
|
||||
|
||||
+93
-4
@@ -1,7 +1,7 @@
|
||||
use goose::conversation::message::{
|
||||
use crate::conversation::message::{
|
||||
ActionRequiredData, Message, MessageContent, ToolNameParts, ToolRequest, ToolResponse,
|
||||
};
|
||||
use goose::utils::safe_truncate;
|
||||
use crate::utils::safe_truncate;
|
||||
use rmcp::model::{ContentBlock, ResourceContents, Role};
|
||||
use serde_json::Value;
|
||||
|
||||
@@ -446,11 +446,65 @@ fn message_to_markdown_for_audience(
|
||||
md.trim_end_matches("\n").to_string()
|
||||
}
|
||||
|
||||
pub fn export_session_to_markdown(messages: Vec<Message>, session_name: &str) -> String {
|
||||
let mut markdown_output = String::new();
|
||||
|
||||
markdown_output.push_str(&format!("# Session Export: {}\n\n", session_name));
|
||||
|
||||
if messages.is_empty() {
|
||||
markdown_output.push_str("*(This session has no messages)*\n");
|
||||
return markdown_output;
|
||||
}
|
||||
|
||||
markdown_output.push_str(&format!("*Total messages: {}*\n\n---\n\n", messages.len()));
|
||||
|
||||
let mut skip_next_if_tool_response = false;
|
||||
|
||||
for message in &messages {
|
||||
let is_only_tool_response = message.role == Role::User
|
||||
&& message
|
||||
.content
|
||||
.iter()
|
||||
.all(|content| matches!(content, MessageContent::ToolResponse(_)));
|
||||
|
||||
if skip_next_if_tool_response && is_only_tool_response {
|
||||
markdown_output.push_str(&user_projected_message_to_markdown(message));
|
||||
markdown_output.push_str("\n\n---\n\n");
|
||||
skip_next_if_tool_response = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
skip_next_if_tool_response = false;
|
||||
|
||||
if !is_only_tool_response {
|
||||
let role_prefix = match message.role {
|
||||
Role::User => "### User:\n",
|
||||
Role::Assistant => "### Assistant:\n",
|
||||
};
|
||||
markdown_output.push_str(role_prefix);
|
||||
}
|
||||
|
||||
markdown_output.push_str(&user_projected_message_to_markdown(message));
|
||||
markdown_output.push_str("\n\n---\n\n");
|
||||
|
||||
if message
|
||||
.content
|
||||
.iter()
|
||||
.any(|content| matches!(content, MessageContent::ToolRequest(_)))
|
||||
{
|
||||
skip_next_if_tool_response = true;
|
||||
}
|
||||
}
|
||||
|
||||
markdown_output
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use goose::conversation::message::{Message, ToolRequest, ToolResponse};
|
||||
use rmcp::model::{CallToolRequestParams, ContentBlock, TextContent};
|
||||
use crate::conversation::message::{Message, ToolRequest, ToolResponse};
|
||||
use crate::conversation::Conversation;
|
||||
use rmcp::model::{Annotations, CallToolRequestParams, ContentBlock, TextContent};
|
||||
use rmcp::object;
|
||||
use serde_json::json;
|
||||
|
||||
@@ -1139,4 +1193,39 @@ found 0 vulnerabilities"#;
|
||||
assert!(response_result.contains("added 57 packages"));
|
||||
assert!(response_result.contains("found 0 vulnerabilities"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn markdown_export_preserves_user_audience_tool_output() {
|
||||
let user_output = ContentBlock::Text(
|
||||
TextContent::new("user-visible output")
|
||||
.with_annotations(Annotations::default().with_audience(vec![Role::User])),
|
||||
);
|
||||
let assistant_output = ContentBlock::Text(
|
||||
TextContent::new("assistant-only output")
|
||||
.with_annotations(Annotations::default().with_audience(vec![Role::Assistant])),
|
||||
);
|
||||
let conversation = Conversation::new_unvalidated([Message::user().with_tool_response(
|
||||
"tool-1",
|
||||
Ok(rmcp::model::CallToolResult::success(vec![
|
||||
user_output,
|
||||
assistant_output,
|
||||
ContentBlock::text("shared output"),
|
||||
])),
|
||||
)]);
|
||||
|
||||
let markdown =
|
||||
export_session_to_markdown(conversation.user_visible_messages(), "Audience export");
|
||||
|
||||
assert!(markdown.contains("user-visible output"));
|
||||
assert!(markdown.contains("shared output"));
|
||||
assert!(!markdown.contains("assistant-only output"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn markdown_export_handles_empty_conversation() {
|
||||
let markdown = export_session_to_markdown(Vec::new(), "Empty session");
|
||||
|
||||
assert!(markdown.contains("# Session Export: Empty session"));
|
||||
assert!(markdown.contains("*(This session has no messages)*"));
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
mod chat_history_search;
|
||||
mod diagnostics;
|
||||
mod export_markdown;
|
||||
pub mod extension_data;
|
||||
pub mod import_formats;
|
||||
mod last_message_snippet;
|
||||
@@ -15,6 +16,9 @@ pub use diagnostics::{
|
||||
DiagnosticsLevel, DiagnosticsLogs, DiagnosticsPrompt, DiagnosticsReport,
|
||||
DiagnosticsScheduledRecipe, DiagnosticsTextFile, SystemInfo,
|
||||
};
|
||||
pub use export_markdown::{
|
||||
export_session_to_markdown, message_to_markdown, user_projected_message_to_markdown,
|
||||
};
|
||||
pub use extension_data::{EnabledExtensionsState, ExtensionData, ExtensionState, TodoState};
|
||||
pub use session_manager::{
|
||||
Session, SessionInsights, SessionManager, SessionNameUpdate, SessionType, SessionUpdateBuilder,
|
||||
|
||||
@@ -5,6 +5,7 @@ use crate::conversation::Conversation;
|
||||
use crate::providers::base::CostSource;
|
||||
use crate::providers::base::Provider;
|
||||
use crate::recipe::Recipe;
|
||||
use crate::session::export_markdown::export_session_to_markdown;
|
||||
use crate::session::extension_data::ExtensionData;
|
||||
use crate::session::session_naming::{
|
||||
generate_session_name, MSG_COUNT_FOR_SESSION_NAME_GENERATION,
|
||||
@@ -489,6 +490,15 @@ impl SessionManager {
|
||||
self.storage.export_session(id).await
|
||||
}
|
||||
|
||||
pub async fn export_session_markdown(&self, id: &str) -> Result<String> {
|
||||
let session = self.get_session(id, true).await?;
|
||||
let messages = session
|
||||
.conversation
|
||||
.map(|conversation| conversation.user_visible_messages())
|
||||
.unwrap_or_default();
|
||||
Ok(export_session_to_markdown(messages, &session.name))
|
||||
}
|
||||
|
||||
pub async fn import_session(
|
||||
&self,
|
||||
json: &str,
|
||||
|
||||
@@ -5,7 +5,7 @@ import type {
|
||||
NewSessionRequest,
|
||||
SessionInfo,
|
||||
} from '@agentclientprotocol/sdk';
|
||||
import type { GooseExtension, SessionImportSource } from '@aaif/goose-sdk';
|
||||
import type { GooseExtension, SessionExportFormat, SessionImportSource } from '@aaif/goose-sdk';
|
||||
import { getAcpClient } from './acpConnection';
|
||||
import type { ExtensionLoadResult } from '../types/extensions';
|
||||
import type { Session } from '../types/session';
|
||||
@@ -295,16 +295,16 @@ export async function acpForkSession(
|
||||
return String(response.sessionId);
|
||||
}
|
||||
|
||||
export async function acpExportSession(sessionId: string): Promise<string> {
|
||||
export async function acpExportSession(
|
||||
sessionId: string,
|
||||
format: SessionExportFormat = 'json'
|
||||
): Promise<string> {
|
||||
const client = await getAcpClient();
|
||||
const response = await client.goose.sessionExport_unstable({ sessionId });
|
||||
const response = await client.goose.sessionExport_unstable({ sessionId, format });
|
||||
return response.data;
|
||||
}
|
||||
|
||||
export async function acpImportSession(
|
||||
input: string,
|
||||
source: SessionImportSource
|
||||
): Promise<void> {
|
||||
export async function acpImportSession(input: string, source: SessionImportSource): Promise<void> {
|
||||
const client = await getAcpClient();
|
||||
await client.goose.sessionImport_unstable({ input, source });
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -3770,6 +3770,12 @@
|
||||
"sessions.action.export": {
|
||||
"defaultMessage": "Sitzung exportieren"
|
||||
},
|
||||
"sessions.action.exportJson": {
|
||||
"defaultMessage": "JSON"
|
||||
},
|
||||
"sessions.action.exportMarkdown": {
|
||||
"defaultMessage": "Markdown"
|
||||
},
|
||||
"sessions.action.openNewWindow": {
|
||||
"defaultMessage": "In neuem Fenster öffnen"
|
||||
},
|
||||
@@ -3869,6 +3875,9 @@
|
||||
"sessions.toast.duplicated": {
|
||||
"defaultMessage": "Sitzung \"{name}\" erfolgreich dupliziert"
|
||||
},
|
||||
"sessions.toast.exportFailed": {
|
||||
"defaultMessage": "Sitzung konnte nicht exportiert werden: {error}"
|
||||
},
|
||||
"sessions.toast.exported": {
|
||||
"defaultMessage": "Sitzung erfolgreich exportiert"
|
||||
},
|
||||
|
||||
@@ -3770,6 +3770,12 @@
|
||||
"sessions.action.export": {
|
||||
"defaultMessage": "Export session"
|
||||
},
|
||||
"sessions.action.exportJson": {
|
||||
"defaultMessage": "JSON"
|
||||
},
|
||||
"sessions.action.exportMarkdown": {
|
||||
"defaultMessage": "Markdown"
|
||||
},
|
||||
"sessions.action.openNewWindow": {
|
||||
"defaultMessage": "Open in new window"
|
||||
},
|
||||
@@ -3869,6 +3875,9 @@
|
||||
"sessions.toast.duplicated": {
|
||||
"defaultMessage": "Session \"{name}\" duplicated successfully"
|
||||
},
|
||||
"sessions.toast.exportFailed": {
|
||||
"defaultMessage": "Failed to export session: {error}"
|
||||
},
|
||||
"sessions.toast.exported": {
|
||||
"defaultMessage": "Session exported successfully"
|
||||
},
|
||||
|
||||
@@ -3770,6 +3770,12 @@
|
||||
"sessions.action.export": {
|
||||
"defaultMessage": "Exportar sesión"
|
||||
},
|
||||
"sessions.action.exportJson": {
|
||||
"defaultMessage": "JSON"
|
||||
},
|
||||
"sessions.action.exportMarkdown": {
|
||||
"defaultMessage": "Markdown"
|
||||
},
|
||||
"sessions.action.openNewWindow": {
|
||||
"defaultMessage": "Abrir en una ventana nueva"
|
||||
},
|
||||
@@ -3869,6 +3875,9 @@
|
||||
"sessions.toast.duplicated": {
|
||||
"defaultMessage": "Sesión \"{name}\" duplicada correctamente"
|
||||
},
|
||||
"sessions.toast.exportFailed": {
|
||||
"defaultMessage": "No se pudo exportar la sesión: {error}"
|
||||
},
|
||||
"sessions.toast.exported": {
|
||||
"defaultMessage": "Sesión exportada correctamente"
|
||||
},
|
||||
|
||||
@@ -3770,6 +3770,12 @@
|
||||
"sessions.action.export": {
|
||||
"defaultMessage": "Exporter la session"
|
||||
},
|
||||
"sessions.action.exportJson": {
|
||||
"defaultMessage": "JSON"
|
||||
},
|
||||
"sessions.action.exportMarkdown": {
|
||||
"defaultMessage": "Markdown"
|
||||
},
|
||||
"sessions.action.openNewWindow": {
|
||||
"defaultMessage": "Ouvrir dans une nouvelle fenêtre"
|
||||
},
|
||||
@@ -3869,6 +3875,9 @@
|
||||
"sessions.toast.duplicated": {
|
||||
"defaultMessage": "Session « {name} » dupliquée avec succès"
|
||||
},
|
||||
"sessions.toast.exportFailed": {
|
||||
"defaultMessage": "Échec de l'exportation de la session : {error}"
|
||||
},
|
||||
"sessions.toast.exported": {
|
||||
"defaultMessage": "Session exportée avec succès"
|
||||
},
|
||||
|
||||
@@ -3770,6 +3770,12 @@
|
||||
"sessions.action.export": {
|
||||
"defaultMessage": "निर्यात सत्र"
|
||||
},
|
||||
"sessions.action.exportJson": {
|
||||
"defaultMessage": "JSON"
|
||||
},
|
||||
"sessions.action.exportMarkdown": {
|
||||
"defaultMessage": "Markdown"
|
||||
},
|
||||
"sessions.action.openNewWindow": {
|
||||
"defaultMessage": "नई विंडो में खोलें"
|
||||
},
|
||||
@@ -3869,6 +3875,9 @@
|
||||
"sessions.toast.duplicated": {
|
||||
"defaultMessage": "सत्र \"{name}\" सफलतापूर्वक दोहराया गया"
|
||||
},
|
||||
"sessions.toast.exportFailed": {
|
||||
"defaultMessage": "सत्र निर्यात करने में विफल: {error}"
|
||||
},
|
||||
"sessions.toast.exported": {
|
||||
"defaultMessage": "सत्र सफलतापूर्वक निर्यात किया गया"
|
||||
},
|
||||
|
||||
@@ -3770,6 +3770,12 @@
|
||||
"sessions.action.export": {
|
||||
"defaultMessage": "Ekspor sesi"
|
||||
},
|
||||
"sessions.action.exportJson": {
|
||||
"defaultMessage": "JSON"
|
||||
},
|
||||
"sessions.action.exportMarkdown": {
|
||||
"defaultMessage": "Markdown"
|
||||
},
|
||||
"sessions.action.openNewWindow": {
|
||||
"defaultMessage": "Buka di jendela baru"
|
||||
},
|
||||
@@ -3869,6 +3875,9 @@
|
||||
"sessions.toast.duplicated": {
|
||||
"defaultMessage": "Sesi \"{name}\" berhasil digandakan"
|
||||
},
|
||||
"sessions.toast.exportFailed": {
|
||||
"defaultMessage": "Gagal mengekspor sesi: {error}"
|
||||
},
|
||||
"sessions.toast.exported": {
|
||||
"defaultMessage": "Sesi berhasil diekspor"
|
||||
},
|
||||
|
||||
@@ -3770,6 +3770,12 @@
|
||||
"sessions.action.export": {
|
||||
"defaultMessage": "Esporta sessione"
|
||||
},
|
||||
"sessions.action.exportJson": {
|
||||
"defaultMessage": "JSON"
|
||||
},
|
||||
"sessions.action.exportMarkdown": {
|
||||
"defaultMessage": "Markdown"
|
||||
},
|
||||
"sessions.action.openNewWindow": {
|
||||
"defaultMessage": "Apri in una nuova finestra"
|
||||
},
|
||||
@@ -3869,6 +3875,9 @@
|
||||
"sessions.toast.duplicated": {
|
||||
"defaultMessage": "Sessione \"{name}\" duplicata correttamente"
|
||||
},
|
||||
"sessions.toast.exportFailed": {
|
||||
"defaultMessage": "Impossibile esportare la sessione: {error}"
|
||||
},
|
||||
"sessions.toast.exported": {
|
||||
"defaultMessage": "Sessione esportata correttamente"
|
||||
},
|
||||
|
||||
@@ -3770,6 +3770,12 @@
|
||||
"sessions.action.export": {
|
||||
"defaultMessage": "セッションをエクスポート"
|
||||
},
|
||||
"sessions.action.exportJson": {
|
||||
"defaultMessage": "JSON"
|
||||
},
|
||||
"sessions.action.exportMarkdown": {
|
||||
"defaultMessage": "Markdown"
|
||||
},
|
||||
"sessions.action.openNewWindow": {
|
||||
"defaultMessage": "新しいウィンドウで開く"
|
||||
},
|
||||
@@ -3869,6 +3875,9 @@
|
||||
"sessions.toast.duplicated": {
|
||||
"defaultMessage": "セッション「{name}」を複製しました"
|
||||
},
|
||||
"sessions.toast.exportFailed": {
|
||||
"defaultMessage": "セッションをエクスポートできませんでした: {error}"
|
||||
},
|
||||
"sessions.toast.exported": {
|
||||
"defaultMessage": "セッションをエクスポートしました"
|
||||
},
|
||||
|
||||
@@ -3770,6 +3770,12 @@
|
||||
"sessions.action.export": {
|
||||
"defaultMessage": "세션 내보내기"
|
||||
},
|
||||
"sessions.action.exportJson": {
|
||||
"defaultMessage": "JSON"
|
||||
},
|
||||
"sessions.action.exportMarkdown": {
|
||||
"defaultMessage": "Markdown"
|
||||
},
|
||||
"sessions.action.openNewWindow": {
|
||||
"defaultMessage": "새 창에서 열기"
|
||||
},
|
||||
@@ -3869,6 +3875,9 @@
|
||||
"sessions.toast.duplicated": {
|
||||
"defaultMessage": "세션 \"{name}\"이(가) 성공적으로 복제되었습니다."
|
||||
},
|
||||
"sessions.toast.exportFailed": {
|
||||
"defaultMessage": "세션 내보내기 실패: {error}"
|
||||
},
|
||||
"sessions.toast.exported": {
|
||||
"defaultMessage": "세션을 성공적으로 내보냈습니다."
|
||||
},
|
||||
|
||||
@@ -3770,6 +3770,12 @@
|
||||
"sessions.action.export": {
|
||||
"defaultMessage": "Eksport sesi"
|
||||
},
|
||||
"sessions.action.exportJson": {
|
||||
"defaultMessage": "JSON"
|
||||
},
|
||||
"sessions.action.exportMarkdown": {
|
||||
"defaultMessage": "Markdown"
|
||||
},
|
||||
"sessions.action.openNewWindow": {
|
||||
"defaultMessage": "Buka dalam tetingkap baharu"
|
||||
},
|
||||
@@ -3869,6 +3875,9 @@
|
||||
"sessions.toast.duplicated": {
|
||||
"defaultMessage": "Sesi \"{name}\" berjaya diduplikasi"
|
||||
},
|
||||
"sessions.toast.exportFailed": {
|
||||
"defaultMessage": "Gagal mengeksport sesi: {error}"
|
||||
},
|
||||
"sessions.toast.exported": {
|
||||
"defaultMessage": "Sesi berjaya dieksport"
|
||||
},
|
||||
|
||||
@@ -3770,6 +3770,12 @@
|
||||
"sessions.action.export": {
|
||||
"defaultMessage": "Exportar sessão"
|
||||
},
|
||||
"sessions.action.exportJson": {
|
||||
"defaultMessage": "JSON"
|
||||
},
|
||||
"sessions.action.exportMarkdown": {
|
||||
"defaultMessage": "Markdown"
|
||||
},
|
||||
"sessions.action.openNewWindow": {
|
||||
"defaultMessage": "Abrir numa nova janela"
|
||||
},
|
||||
@@ -3869,6 +3875,9 @@
|
||||
"sessions.toast.duplicated": {
|
||||
"defaultMessage": "Sessão \"{name}\" duplicada com êxito"
|
||||
},
|
||||
"sessions.toast.exportFailed": {
|
||||
"defaultMessage": "Falha ao exportar a sessão: {error}"
|
||||
},
|
||||
"sessions.toast.exported": {
|
||||
"defaultMessage": "Sessão exportada com êxito"
|
||||
},
|
||||
|
||||
@@ -3770,6 +3770,12 @@
|
||||
"sessions.action.export": {
|
||||
"defaultMessage": "Экспортировать сеанс"
|
||||
},
|
||||
"sessions.action.exportJson": {
|
||||
"defaultMessage": "JSON"
|
||||
},
|
||||
"sessions.action.exportMarkdown": {
|
||||
"defaultMessage": "Markdown"
|
||||
},
|
||||
"sessions.action.openNewWindow": {
|
||||
"defaultMessage": "Открыть в новом окне"
|
||||
},
|
||||
@@ -3869,6 +3875,9 @@
|
||||
"sessions.toast.duplicated": {
|
||||
"defaultMessage": "Сеанс «{name}» успешно дублирован"
|
||||
},
|
||||
"sessions.toast.exportFailed": {
|
||||
"defaultMessage": "Не удалось экспортировать сеанс: {error}"
|
||||
},
|
||||
"sessions.toast.exported": {
|
||||
"defaultMessage": "Сеанс успешно экспортирован"
|
||||
},
|
||||
|
||||
@@ -3770,6 +3770,12 @@
|
||||
"sessions.action.export": {
|
||||
"defaultMessage": "Oturumu dışa aktar"
|
||||
},
|
||||
"sessions.action.exportJson": {
|
||||
"defaultMessage": "JSON"
|
||||
},
|
||||
"sessions.action.exportMarkdown": {
|
||||
"defaultMessage": "Markdown"
|
||||
},
|
||||
"sessions.action.openNewWindow": {
|
||||
"defaultMessage": "Yeni pencerede aç"
|
||||
},
|
||||
@@ -3869,6 +3875,9 @@
|
||||
"sessions.toast.duplicated": {
|
||||
"defaultMessage": "\"{name}\" oturumu başarıyla kopyalandı"
|
||||
},
|
||||
"sessions.toast.exportFailed": {
|
||||
"defaultMessage": "Oturum dışa aktarılamadı: {error}"
|
||||
},
|
||||
"sessions.toast.exported": {
|
||||
"defaultMessage": "Oturum başarıyla dışa aktarıldı"
|
||||
},
|
||||
|
||||
@@ -3770,6 +3770,12 @@
|
||||
"sessions.action.export": {
|
||||
"defaultMessage": "Xuất phiên"
|
||||
},
|
||||
"sessions.action.exportJson": {
|
||||
"defaultMessage": "JSON"
|
||||
},
|
||||
"sessions.action.exportMarkdown": {
|
||||
"defaultMessage": "Markdown"
|
||||
},
|
||||
"sessions.action.openNewWindow": {
|
||||
"defaultMessage": "Mở trong cửa sổ mới"
|
||||
},
|
||||
@@ -3869,6 +3875,9 @@
|
||||
"sessions.toast.duplicated": {
|
||||
"defaultMessage": "Đã nhân bản phiên làm việc \"{name}\" thành công"
|
||||
},
|
||||
"sessions.toast.exportFailed": {
|
||||
"defaultMessage": "Không thể xuất phiên làm việc: {error}"
|
||||
},
|
||||
"sessions.toast.exported": {
|
||||
"defaultMessage": "Đã xuất phiên làm việc thành công"
|
||||
},
|
||||
|
||||
@@ -3770,6 +3770,12 @@
|
||||
"sessions.action.export": {
|
||||
"defaultMessage": "导出会话"
|
||||
},
|
||||
"sessions.action.exportJson": {
|
||||
"defaultMessage": "JSON"
|
||||
},
|
||||
"sessions.action.exportMarkdown": {
|
||||
"defaultMessage": "Markdown"
|
||||
},
|
||||
"sessions.action.openNewWindow": {
|
||||
"defaultMessage": "在新窗口中打开"
|
||||
},
|
||||
@@ -3869,6 +3875,9 @@
|
||||
"sessions.toast.duplicated": {
|
||||
"defaultMessage": "会话“{name}”复制成功"
|
||||
},
|
||||
"sessions.toast.exportFailed": {
|
||||
"defaultMessage": "导出会话失败:{error}"
|
||||
},
|
||||
"sessions.toast.exported": {
|
||||
"defaultMessage": "会话导出成功"
|
||||
},
|
||||
|
||||
@@ -3770,6 +3770,12 @@
|
||||
"sessions.action.export": {
|
||||
"defaultMessage": "匯出工作階段"
|
||||
},
|
||||
"sessions.action.exportJson": {
|
||||
"defaultMessage": "JSON"
|
||||
},
|
||||
"sessions.action.exportMarkdown": {
|
||||
"defaultMessage": "Markdown"
|
||||
},
|
||||
"sessions.action.openNewWindow": {
|
||||
"defaultMessage": "在新視窗中開啟"
|
||||
},
|
||||
@@ -3869,6 +3875,9 @@
|
||||
"sessions.toast.duplicated": {
|
||||
"defaultMessage": "已成功複製工作階段「{name}」"
|
||||
},
|
||||
"sessions.toast.exportFailed": {
|
||||
"defaultMessage": "無法匯出工作階段:{error}"
|
||||
},
|
||||
"sessions.toast.exported": {
|
||||
"defaultMessage": "已成功匯出工作階段"
|
||||
},
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -1477,14 +1477,18 @@ export type OnboardingImportApplyResponse_unstable = {
|
||||
};
|
||||
|
||||
/**
|
||||
* Export a session as a JSON string.
|
||||
* Export a session as a JSON or markdown string.
|
||||
*/
|
||||
export type ExportSessionRequest_unstable = {
|
||||
sessionId: string;
|
||||
format?: SessionExportFormat;
|
||||
};
|
||||
|
||||
export type SessionExportFormat = 'json' | 'markdown';
|
||||
|
||||
/**
|
||||
* Export session response — raw JSON of the goose session with `conversation`.
|
||||
* Export session response — raw JSON of the goose session with `conversation`,
|
||||
* or a markdown transcript when `format` is `markdown`.
|
||||
*/
|
||||
export type ExportSessionResponse_unstable = {
|
||||
data: string;
|
||||
|
||||
@@ -1410,15 +1410,19 @@ export const zOnboardingImportApplyResponse_unstable = z.object({
|
||||
]).optional()
|
||||
});
|
||||
|
||||
export const zSessionExportFormat = z.enum(['json', 'markdown']);
|
||||
|
||||
/**
|
||||
* Export a session as a JSON string.
|
||||
* Export a session as a JSON or markdown string.
|
||||
*/
|
||||
export const zExportSessionRequest_unstable = z.object({
|
||||
sessionId: z.string()
|
||||
sessionId: z.string(),
|
||||
format: zSessionExportFormat.optional().default('json')
|
||||
});
|
||||
|
||||
/**
|
||||
* Export session response — raw JSON of the goose session with `conversation`.
|
||||
* Export session response — raw JSON of the goose session with `conversation`,
|
||||
* or a markdown transcript when `format` is `markdown`.
|
||||
*/
|
||||
export const zExportSessionResponse_unstable = z.object({
|
||||
data: z.string()
|
||||
|
||||
Reference in New Issue
Block a user