Synced from monorepo
Changes: - Non-blocking coding-data sharing upsell banner - Consolidate remediation in Doctor - Auto mode defers fail-closed gate asks to the classifier - Coalesce marketplace list fetches - Allow removing a marketplace source by name - Contain hung git marketplace sources (timeouts, non-blocking refresh, unbrick modal) - Label failed workspace RPCs with error_kind - Drop redundant explicit tonic/prost deps from xai-grok-shell - Report real exit codes for completed background shells - Narrow the date-rollover reminder to date-bearing templates - Wire toolOverrides through the session and agent - Security: Bash(git:*) allowlist matches whole command chain by prefix - Split prompt-trigger telemetry and record classifier provenance - Raise connectors-manager timeout to 60s - Auto classifier honors recorded approvals for repeat actions - Apply doctor fixes in the TUI - Auto-mode classifier timeouts prompt instead of silently denying - Scope subagent completion drains to the owning session - Add the toolOverrides wire types - Set client_identifier=grok-agent-sdk - Accept both spellings of the workspace-teleport kill switch - Persist one-shot occurrence journal - Stop turns that poll the exact same tool call 16x in a row - Copy compaction checkpoint files when forking sessions - Auto-focus permission prompt from scrollback - Esc cancels the running turn in non-vim and minimal modes - List Ctrl+Z undo and redo in keyboard shortcuts - Out-of-process macOS mic capture - Show active auth mode on session-info - Install the npm binary under $GROK_HOME - Remove hover/click dead zones between dashboard items - Route startup warnings to doctor - Document [feedback.user] author identity config - Extend bang command timeout - Close combine-queued edit-hold race - Integrate relocation recovery - Expose privacy notice rollout flag - Break harness discovery ref cycle so connections can idle-evict - Shift/Alt+Enter inserts newline when editing a queued prompt - Gate project Claude permissions on folder trust - Echo response.create.event_id on response.created - Toast when session creation fails from disk full - Add shared test process lifecycle - Enable dynamic workflows by default - Add relocation transaction state machine - Add shared test sandbox - Surface auth failures on model-switch compact - Persist durable scheduler expiry - Confirm before removing extensions-modal items - Re-run compact and prompt after login when compact hit expired auth - Recap sends hosted tools under backend search
This commit is contained in:
parent
3af4d5d398
commit
a5727c5960
482 changed files with 37627 additions and 13402 deletions
|
|
@ -155,7 +155,10 @@ impl WebSearchClient {
|
|||
return Err(xai_tool_runtime::ToolError::unauthorized(format!(
|
||||
"Responses API returned 401 Unauthorized: {body}"
|
||||
))
|
||||
.with_details(serde_json::json!({ "tool_id" : "web_search", "status" : 401, })));
|
||||
.with_details(serde_json::json!({
|
||||
"tool_id": "web_search",
|
||||
"status": 401,
|
||||
})));
|
||||
}
|
||||
if !status.is_success() {
|
||||
let body = response
|
||||
|
|
@ -243,7 +246,10 @@ impl WebSearchClient {
|
|||
return Err(xai_tool_runtime::ToolError::unauthorized(format!(
|
||||
"Responses API returned 401 Unauthorized: {body}"
|
||||
))
|
||||
.with_details(serde_json::json!({ "tool_id" : "web_search", "status" : 401, })));
|
||||
.with_details(serde_json::json!({
|
||||
"tool_id": "web_search",
|
||||
"status": 401,
|
||||
})));
|
||||
}
|
||||
if !status.is_success() {
|
||||
let body = response
|
||||
|
|
@ -411,26 +417,56 @@ mod tests {
|
|||
}
|
||||
#[test]
|
||||
fn test_extract_citations_empty_response() {
|
||||
let response = response_from_json(serde_json::json!(
|
||||
{ "id" : "resp_test", "object" : "response", "created_at" : 1234567890,
|
||||
"status" : "completed", "output" : [], "model" : "test-model" }
|
||||
));
|
||||
let response = response_from_json(serde_json::json!({
|
||||
"id": "resp_test",
|
||||
"object": "response",
|
||||
"created_at": 1234567890,
|
||||
"status": "completed",
|
||||
"output": [],
|
||||
"model": "test-model"
|
||||
}));
|
||||
let citations = extract_citations(&response);
|
||||
assert!(citations.is_empty());
|
||||
}
|
||||
#[test]
|
||||
fn test_extract_citations_with_url_citations() {
|
||||
let response = response_from_json(serde_json::json!(
|
||||
{ "id" : "resp_test", "object" : "response", "created_at" : 1234567890,
|
||||
"status" : "completed", "model" : "test-model", "output" : [{ "type" :
|
||||
"message", "id" : "msg_1", "status" : "completed", "role" : "assistant",
|
||||
"content" : [{ "type" : "output_text", "text" :
|
||||
"Here is some info about Rust.", "annotations" : [{ "type" :
|
||||
"url_citation", "url" : "https://www.rust-lang.org/", "title" :
|
||||
"Rust Programming Language", "start_index" : 0, "end_index" : 10 }, {
|
||||
"type" : "url_citation", "url" : "https://docs.rs/", "title" : "Docs.rs",
|
||||
"start_index" : 11, "end_index" : 20 }] }] }] }
|
||||
));
|
||||
let response = response_from_json(serde_json::json!({
|
||||
"id": "resp_test",
|
||||
"object": "response",
|
||||
"created_at": 1234567890,
|
||||
"status": "completed",
|
||||
"model": "test-model",
|
||||
"output": [
|
||||
{
|
||||
"type": "message",
|
||||
"id": "msg_1",
|
||||
"status": "completed",
|
||||
"role": "assistant",
|
||||
"content": [
|
||||
{
|
||||
"type": "output_text",
|
||||
"text": "Here is some info about Rust.",
|
||||
"annotations": [
|
||||
{
|
||||
"type": "url_citation",
|
||||
"url": "https://www.rust-lang.org/",
|
||||
"title": "Rust Programming Language",
|
||||
"start_index": 0,
|
||||
"end_index": 10
|
||||
},
|
||||
{
|
||||
"type": "url_citation",
|
||||
"url": "https://docs.rs/",
|
||||
"title": "Docs.rs",
|
||||
"start_index": 11,
|
||||
"end_index": 20
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}));
|
||||
let citations = extract_citations(&response);
|
||||
assert_eq!(citations.len(), 2);
|
||||
assert_eq!(citations[0], "https://www.rust-lang.org/");
|
||||
|
|
@ -438,19 +474,50 @@ mod tests {
|
|||
}
|
||||
#[test]
|
||||
fn test_extract_citations_deduplicates() {
|
||||
let response = response_from_json(serde_json::json!(
|
||||
{ "id" : "resp_test", "object" : "response", "created_at" : 1234567890,
|
||||
"status" : "completed", "model" : "test-model", "output" : [{ "type" :
|
||||
"message", "id" : "msg_1", "status" : "completed", "role" : "assistant",
|
||||
"content" : [{ "type" : "output_text", "text" :
|
||||
"Info with duplicate citations.", "annotations" : [{ "type" :
|
||||
"url_citation", "url" : "https://example.com/page1", "title" : "Page 1",
|
||||
"start_index" : 0, "end_index" : 5 }, { "type" : "url_citation", "url" :
|
||||
"https://example.com/page2", "title" : "Page 2", "start_index" : 6,
|
||||
"end_index" : 10 }, { "type" : "url_citation", "url" :
|
||||
"https://example.com/page1", "title" : "Page 1 Again", "start_index" :
|
||||
11, "end_index" : 15 }] }] }] }
|
||||
));
|
||||
let response = response_from_json(serde_json::json!({
|
||||
"id": "resp_test",
|
||||
"object": "response",
|
||||
"created_at": 1234567890,
|
||||
"status": "completed",
|
||||
"model": "test-model",
|
||||
"output": [
|
||||
{
|
||||
"type": "message",
|
||||
"id": "msg_1",
|
||||
"status": "completed",
|
||||
"role": "assistant",
|
||||
"content": [
|
||||
{
|
||||
"type": "output_text",
|
||||
"text": "Info with duplicate citations.",
|
||||
"annotations": [
|
||||
{
|
||||
"type": "url_citation",
|
||||
"url": "https://example.com/page1",
|
||||
"title": "Page 1",
|
||||
"start_index": 0,
|
||||
"end_index": 5
|
||||
},
|
||||
{
|
||||
"type": "url_citation",
|
||||
"url": "https://example.com/page2",
|
||||
"title": "Page 2",
|
||||
"start_index": 6,
|
||||
"end_index": 10
|
||||
},
|
||||
{
|
||||
"type": "url_citation",
|
||||
"url": "https://example.com/page1",
|
||||
"title": "Page 1 Again",
|
||||
"start_index": 11,
|
||||
"end_index": 15
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}));
|
||||
let citations = extract_citations(&response);
|
||||
assert_eq!(citations.len(), 2);
|
||||
assert_eq!(citations[0], "https://example.com/page1");
|
||||
|
|
@ -458,19 +525,57 @@ mod tests {
|
|||
}
|
||||
#[test]
|
||||
fn test_extract_citations_multiple_messages() {
|
||||
let response = response_from_json(serde_json::json!(
|
||||
{ "id" : "resp_test", "object" : "response", "created_at" : 1234567890,
|
||||
"status" : "completed", "model" : "test-model", "output" : [{ "type" :
|
||||
"message", "id" : "msg_1", "status" : "completed", "role" : "assistant",
|
||||
"content" : [{ "type" : "output_text", "text" : "First message",
|
||||
"annotations" : [{ "type" : "url_citation", "url" : "https://first.com/",
|
||||
"title" : "First", "start_index" : 0, "end_index" : 5 }] }] }, { "type" :
|
||||
"message", "id" : "msg_2", "status" : "completed", "role" : "assistant",
|
||||
"content" : [{ "type" : "output_text", "text" : "Second message",
|
||||
"annotations" : [{ "type" : "url_citation", "url" :
|
||||
"https://second.com/", "title" : "Second", "start_index" : 0, "end_index"
|
||||
: 6 }] }] }] }
|
||||
));
|
||||
let response = response_from_json(serde_json::json!({
|
||||
"id": "resp_test",
|
||||
"object": "response",
|
||||
"created_at": 1234567890,
|
||||
"status": "completed",
|
||||
"model": "test-model",
|
||||
"output": [
|
||||
{
|
||||
"type": "message",
|
||||
"id": "msg_1",
|
||||
"status": "completed",
|
||||
"role": "assistant",
|
||||
"content": [
|
||||
{
|
||||
"type": "output_text",
|
||||
"text": "First message",
|
||||
"annotations": [
|
||||
{
|
||||
"type": "url_citation",
|
||||
"url": "https://first.com/",
|
||||
"title": "First",
|
||||
"start_index": 0,
|
||||
"end_index": 5
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "message",
|
||||
"id": "msg_2",
|
||||
"status": "completed",
|
||||
"role": "assistant",
|
||||
"content": [
|
||||
{
|
||||
"type": "output_text",
|
||||
"text": "Second message",
|
||||
"annotations": [
|
||||
{
|
||||
"type": "url_citation",
|
||||
"url": "https://second.com/",
|
||||
"title": "Second",
|
||||
"start_index": 0,
|
||||
"end_index": 6
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}));
|
||||
let citations = extract_citations(&response);
|
||||
assert_eq!(citations.len(), 2);
|
||||
assert_eq!(citations[0], "https://first.com/");
|
||||
|
|
@ -478,14 +583,36 @@ mod tests {
|
|||
}
|
||||
#[test]
|
||||
fn test_extract_citations_ignores_non_url_annotations() {
|
||||
let response = response_from_json(serde_json::json!(
|
||||
{ "id" : "resp_test", "object" : "response", "created_at" : 1234567890,
|
||||
"status" : "completed", "model" : "test-model", "output" : [{ "type" :
|
||||
"message", "id" : "msg_1", "status" : "completed", "role" : "assistant",
|
||||
"content" : [{ "type" : "output_text", "text" : "Some text",
|
||||
"annotations" : [{ "type" : "url_citation", "url" : "https://valid.com/",
|
||||
"title" : "Valid", "start_index" : 0, "end_index" : 4 }] }] }] }
|
||||
));
|
||||
let response = response_from_json(serde_json::json!({
|
||||
"id": "resp_test",
|
||||
"object": "response",
|
||||
"created_at": 1234567890,
|
||||
"status": "completed",
|
||||
"model": "test-model",
|
||||
"output": [
|
||||
{
|
||||
"type": "message",
|
||||
"id": "msg_1",
|
||||
"status": "completed",
|
||||
"role": "assistant",
|
||||
"content": [
|
||||
{
|
||||
"type": "output_text",
|
||||
"text": "Some text",
|
||||
"annotations": [
|
||||
{
|
||||
"type": "url_citation",
|
||||
"url": "https://valid.com/",
|
||||
"title": "Valid",
|
||||
"start_index": 0,
|
||||
"end_index": 4
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}));
|
||||
let citations = extract_citations(&response);
|
||||
assert_eq!(citations.len(), 1);
|
||||
assert_eq!(citations[0], "https://valid.com/");
|
||||
|
|
@ -510,14 +637,24 @@ mod tests {
|
|||
Mock::given(method("POST"))
|
||||
.and(path("/responses"))
|
||||
.and(header("Authorization", "Bearer static-key-from-config"))
|
||||
.respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!(
|
||||
{ "id" : "resp_test", "object" : "response", "created_at" :
|
||||
1234567890, "status" : "completed", "model" : "test-model",
|
||||
"output" : [{ "type" : "message", "id" : "msg_1", "status" :
|
||||
"completed", "role" : "assistant", "content" : [{ "type" :
|
||||
"output_text", "text" : "search result", "annotations" : []
|
||||
}] }] }
|
||||
)))
|
||||
.respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!({
|
||||
"id": "resp_test",
|
||||
"object": "response",
|
||||
"created_at": 1234567890,
|
||||
"status": "completed",
|
||||
"model": "test-model",
|
||||
"output": [{
|
||||
"type": "message",
|
||||
"id": "msg_1",
|
||||
"status": "completed",
|
||||
"role": "assistant",
|
||||
"content": [{
|
||||
"type": "output_text",
|
||||
"text": "search result",
|
||||
"annotations": []
|
||||
}]
|
||||
}]
|
||||
})))
|
||||
.mount(&server)
|
||||
.await;
|
||||
let config = WebSearchConfig::Enabled {
|
||||
|
|
@ -550,14 +687,24 @@ mod tests {
|
|||
Mock::given(method("POST"))
|
||||
.and(path("/responses"))
|
||||
.and(header("Authorization", "Bearer fresh-key-from-provider"))
|
||||
.respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!(
|
||||
{ "id" : "resp_test", "object" : "response", "created_at" :
|
||||
1234567890, "status" : "completed", "model" : "test-model",
|
||||
"output" : [{ "type" : "message", "id" : "msg_1", "status" :
|
||||
"completed", "role" : "assistant", "content" : [{ "type" :
|
||||
"output_text", "text" : "fresh result", "annotations" : [] }]
|
||||
}] }
|
||||
)))
|
||||
.respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!({
|
||||
"id": "resp_test",
|
||||
"object": "response",
|
||||
"created_at": 1234567890,
|
||||
"status": "completed",
|
||||
"model": "test-model",
|
||||
"output": [{
|
||||
"type": "message",
|
||||
"id": "msg_1",
|
||||
"status": "completed",
|
||||
"role": "assistant",
|
||||
"content": [{
|
||||
"type": "output_text",
|
||||
"text": "fresh result",
|
||||
"annotations": []
|
||||
}]
|
||||
}]
|
||||
})))
|
||||
.mount(&server)
|
||||
.await;
|
||||
let config = WebSearchConfig::Enabled {
|
||||
|
|
@ -577,13 +724,28 @@ mod tests {
|
|||
}
|
||||
#[test]
|
||||
fn test_extract_citations_no_annotations() {
|
||||
let response = response_from_json(serde_json::json!(
|
||||
{ "id" : "resp_test", "object" : "response", "created_at" : 1234567890,
|
||||
"status" : "completed", "model" : "test-model", "output" : [{ "type" :
|
||||
"message", "id" : "msg_1", "status" : "completed", "role" : "assistant",
|
||||
"content" : [{ "type" : "output_text", "text" :
|
||||
"Plain text with no annotations", "annotations" : [] }] }] }
|
||||
));
|
||||
let response = response_from_json(serde_json::json!({
|
||||
"id": "resp_test",
|
||||
"object": "response",
|
||||
"created_at": 1234567890,
|
||||
"status": "completed",
|
||||
"model": "test-model",
|
||||
"output": [
|
||||
{
|
||||
"type": "message",
|
||||
"id": "msg_1",
|
||||
"status": "completed",
|
||||
"role": "assistant",
|
||||
"content": [
|
||||
{
|
||||
"type": "output_text",
|
||||
"text": "Plain text with no annotations",
|
||||
"annotations": []
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}));
|
||||
let citations = extract_citations(&response);
|
||||
assert!(citations.is_empty());
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue