Jazzcort add timeout (#6772)

Signed-off-by: Jazzcort <jason101011113@gmail.com>
Co-authored-by: Jazzcort <jason101011113@gmail.com>
Co-authored-by: Douwe Osinga <douwe@squareup.com>
This commit is contained in:
Douwe Osinga
2026-01-28 10:59:20 -05:00
committed by GitHub
parent 4d608690b8
commit a4ad231d60
11 changed files with 68 additions and 30 deletions
+36 -3
View File
@@ -112,6 +112,38 @@ pub struct SessionOptions {
pub container: Option<String>,
}
#[derive(Debug, Clone)]
pub struct StreamableHttpOptions {
pub url: String,
pub timeout: u64,
}
fn parse_streamable_http_extension(input: &str) -> Result<StreamableHttpOptions, String> {
let mut input_iter = input.split_whitespace();
let (mut url, mut timeout) = (String::new(), goose::config::DEFAULT_EXTENSION_TIMEOUT);
if let Some(url_str) = input_iter.next() {
url.push_str(url_str);
}
for kv_pair in input_iter {
if !kv_pair.contains('=') {
continue;
}
let (key, value) = kv_pair.split_once('=').unwrap();
// We Can have more keys here for setting other properties
if key == "timeout" {
if let Ok(seconds) = value.parse::<u64>() {
timeout = seconds;
}
}
}
Ok(StreamableHttpOptions { url, timeout })
}
/// Extension configuration options shared between Session and Run commands
#[derive(Args, Debug, Clone, Default)]
pub struct ExtensionOptions {
@@ -128,10 +160,11 @@ pub struct ExtensionOptions {
long = "with-streamable-http-extension",
value_name = "URL",
help = "Add streamable HTTP extensions (can be specified multiple times)",
long_help = "Add streamable HTTP extensions from a URL. Can be specified multiple times. Format: 'url...'",
action = clap::ArgAction::Append
long_help = "Add streamable HTTP extensions from a URL. Can be specified multiple times. Format: 'url...' or 'url... timeout=100' to set up timeout other than default",
action = clap::ArgAction::Append,
value_parser = parse_streamable_http_extension
)]
pub streamable_http_extensions: Vec<String>,
pub streamable_http_extensions: Vec<StreamableHttpOptions>,
#[arg(
long = "with-builtin",
+10 -1
View File
@@ -1,3 +1,4 @@
use crate::cli::StreamableHttpOptions;
use crate::session::build_session;
use crate::session::SessionBuilderConfig;
use crate::{logging, CliSession};
@@ -34,13 +35,21 @@ pub async fn agent_generator(
requirements: ExtensionRequirements,
session_id: String,
) -> BenchAgent {
let streamable_http_extensions: Vec<StreamableHttpOptions> = requirements
.streamable_http
.iter()
.map(|s| StreamableHttpOptions {
url: s.clone(),
timeout: goose::config::DEFAULT_EXTENSION_TIMEOUT,
})
.collect();
let base_session = build_session(SessionBuilderConfig {
session_id: Some(session_id),
resume: false,
fork: false,
no_session: false,
extensions: requirements.external,
streamable_http_extensions: requirements.streamable_http,
streamable_http_extensions,
builtins: requirements.builtin,
recipe: None,
additional_system_prompt: None,
+8 -2
View File
@@ -1,3 +1,5 @@
use crate::cli::StreamableHttpOptions;
use super::output;
use super::CliSession;
use console::style;
@@ -87,7 +89,7 @@ pub struct SessionBuilderConfig {
/// List of stdio extension commands to add
pub extensions: Vec<String>,
/// List of streamable HTTP extension commands to add
pub streamable_http_extensions: Vec<String>,
pub streamable_http_extensions: Vec<StreamableHttpOptions>,
/// List of builtin extension commands to add
pub builtins: Vec<String>,
/// Recipe for the session
@@ -578,6 +580,7 @@ pub async fn build_session(session_config: SessionBuilderConfig) -> CliSession {
)
.await;
if let Err(e) = session
.agent
.persist_extension_state(&session_id.clone())
@@ -629,7 +632,10 @@ mod tests {
fork: false,
no_session: false,
extensions: vec!["echo test".to_string()],
streamable_http_extensions: vec!["http://localhost:8080/mcp".to_string()],
streamable_http_extensions: vec![StreamableHttpOptions {
url: "http://localhost:8080/mcp".to_string(),
timeout: goose::config::DEFAULT_EXTENSION_TIMEOUT,
}],
builtins: vec!["developer".to_string()],
recipe: None,
additional_system_prompt: Some("Test prompt".to_string()),
+1
View File
@@ -9,6 +9,7 @@ mod prompt;
mod task_execution_display;
mod thinking;
use crate::cli::StreamableHttpOptions;
use crate::session::task_execution_display::{
format_task_execution_notification, TASK_EXECUTION_NOTIFICATION_TYPE,
};
+1 -3
View File
@@ -418,9 +418,7 @@ export function AppInner() {
window.electron.reactReady();
} catch (error) {
console.error('Error sending reactReady:', error);
setFatalError(
`React ready notification failed: ${errorMessage(error, 'Unknown error')}`
);
setFatalError(`React ready notification failed: ${errorMessage(error, 'Unknown error')}`);
}
}, []);
@@ -90,9 +90,7 @@ export const AlertBox = ({ alert, className }: AlertBoxProps) => {
}
} catch (error) {
console.error('Error saving threshold:', error);
window.alert(
`Failed to save threshold: ${errorMessage(error, 'Unknown error')}`
);
window.alert(`Failed to save threshold: ${errorMessage(error, 'Unknown error')}`);
} finally {
setIsSaving(false);
}
@@ -206,9 +206,7 @@ const SchedulesView: React.FC<SchedulesViewProps> = ({ onClose: _onClose }) => {
setSchedules(fetchedSchedules);
} catch (error) {
console.error('Failed to fetch schedules:', error);
setApiError(
errorMessage(error, 'An unknown error occurred while fetching schedules.')
);
setApiError(errorMessage(error, 'An unknown error occurred while fetching schedules.'));
} finally {
setIsLoading(false);
}
@@ -375,8 +373,7 @@ const SchedulesView: React.FC<SchedulesViewProps> = ({ onClose: _onClose }) => {
await fetchSchedules();
} catch (error) {
console.error(`Failed to kill running job "${id}":`, error);
const errorMsg =
errorMessage(error, `Unknown error killing job "${id}".`);
const errorMsg = errorMessage(error, `Unknown error killing job "${id}".`);
setApiError(errorMsg);
toastError({
title: 'Kill Job Error',
@@ -413,8 +410,7 @@ const SchedulesView: React.FC<SchedulesViewProps> = ({ onClose: _onClose }) => {
}
} catch (error) {
console.error(`Failed to inspect running job "${id}":`, error);
const errorMsg =
errorMessage(error, `Unknown error inspecting job "${id}".`);
const errorMsg = errorMessage(error, `Unknown error inspecting job "${id}".`);
setApiError(errorMsg);
toastError({
title: 'Inspect Job Error',
@@ -189,9 +189,7 @@ const SessionHistoryView: React.FC<SessionHistoryViewProps> = ({
setIsShareModalOpen(true);
} catch (error) {
console.error('Error sharing session:', error);
toast.error(
`Failed to share session: ${errorMessage(error, 'Unknown error')}`
);
toast.error(`Failed to share session: ${errorMessage(error, 'Unknown error')}`);
} finally {
setIsSharing(false);
}
@@ -476,7 +476,9 @@ const SessionListView: React.FC<SessionListViewProps> = React.memo(
);
} catch (error) {
console.error('Error deleting session:', error);
toast.error(`Failed to delete session "${sessionName}": ${errorMessage(error, 'Unknown error')}`);
toast.error(
`Failed to delete session "${sessionName}": ${errorMessage(error, 'Unknown error')}`
);
}
await loadSessions();
}, [sessionToDelete, loadSessions]);
+1 -6
View File
@@ -247,12 +247,7 @@ export function registerUpdateIpcHandlers() {
log.error('Error downloading update:', error);
const version = githubUpdateInfo.latestVersion || lastUpdateState?.latestVersion || 'unknown';
const method = isUsingGitHubFallback ? 'github-fallback' : 'electron-updater';
trackUpdateDownloadCompleted(
false,
version,
method,
errorMessage(error, 'unknown')
);
trackUpdateDownloadCompleted(false, version, method, errorMessage(error, 'unknown'));
return {
success: false,
error: errorMessage(error, 'Unknown error'),
+3 -1
View File
@@ -58,7 +58,9 @@ export async function loadSession(sessionId: string, forceRefresh = false): Prom
return session;
} catch (error) {
throw new Error(`Error loading session ${sessionId}: ${errorMessage(error, 'Unknown error')}`);
throw new Error(
`Error loading session ${sessionId}: ${errorMessage(error, 'Unknown error')}`
);
} finally {
inFlightRequests.delete(sessionId);
}