mirror of
https://github.com/harivansh-afk/sandbox-agent.git
synced 2026-04-15 10:05:18 +00:00
feat: add copy button to events tab to copy all events as JSON
This commit is contained in:
parent
6782836030
commit
f2c060903f
8 changed files with 156 additions and 102 deletions
|
|
@ -1,4 +1,4 @@
|
|||
import { ChevronDown, ChevronRight } from "lucide-react";
|
||||
import { ChevronDown, ChevronRight, Copy, Check } from "lucide-react";
|
||||
import { useEffect, useState } from "react";
|
||||
import type { UniversalEvent } from "sandbox-agent";
|
||||
import { formatJson, formatTime } from "../../utils/format";
|
||||
|
|
@ -20,6 +20,17 @@ const EventsTab = ({
|
|||
error: string | null;
|
||||
}) => {
|
||||
const [collapsedEvents, setCollapsedEvents] = useState<Record<string, boolean>>({});
|
||||
const [copied, setCopied] = useState(false);
|
||||
|
||||
const handleCopy = async () => {
|
||||
try {
|
||||
await navigator.clipboard.writeText(JSON.stringify(events, null, 2));
|
||||
setCopied(true);
|
||||
setTimeout(() => setCopied(false), 2000);
|
||||
} catch (err) {
|
||||
console.error("Failed to copy events:", err);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (events.length === 0) {
|
||||
|
|
@ -35,6 +46,15 @@ const EventsTab = ({
|
|||
<button className="button ghost small" onClick={onFetch} disabled={loading}>
|
||||
{loading ? "Loading..." : "Fetch"}
|
||||
</button>
|
||||
<button
|
||||
className="button ghost small"
|
||||
onClick={handleCopy}
|
||||
disabled={events.length === 0}
|
||||
title="Copy all events as JSON"
|
||||
>
|
||||
{copied ? <Check size={14} /> : <Copy size={14} />}
|
||||
{copied ? "Copied" : "Copy"}
|
||||
</button>
|
||||
<button className="button ghost small" onClick={onClear}>
|
||||
Clear
|
||||
</button>
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ use std::io::{BufRead, BufReader, Write};
|
|||
use std::net::TcpListener;
|
||||
use std::path::PathBuf;
|
||||
use std::process::Stdio;
|
||||
use std::sync::atomic::{AtomicI64, Ordering};
|
||||
use std::sync::atomic::{AtomicI64, AtomicU64, Ordering};
|
||||
use std::sync::{Arc, Weak};
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
|
|
@ -46,6 +46,7 @@ use sandbox_agent_agent_management::credentials::{
|
|||
use crate::agent_server_logs::AgentServerLogs;
|
||||
|
||||
const MOCK_EVENT_DELAY_MS: u64 = 200;
|
||||
static USER_MESSAGE_COUNTER: AtomicU64 = AtomicU64::new(1);
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct AppState {
|
||||
|
|
@ -263,6 +264,7 @@ struct SessionState {
|
|||
broadcaster: broadcast::Sender<UniversalEvent>,
|
||||
opencode_stream_started: bool,
|
||||
codex_sender: Option<mpsc::UnboundedSender<String>>,
|
||||
session_started_emitted: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
|
|
@ -315,6 +317,7 @@ impl SessionState {
|
|||
broadcaster,
|
||||
opencode_stream_started: false,
|
||||
codex_sender: None,
|
||||
session_started_emitted: false,
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -394,6 +397,18 @@ impl SessionState {
|
|||
}
|
||||
|
||||
fn push_event(&mut self, conversion: EventConversion) -> Option<UniversalEvent> {
|
||||
if conversion.event_type == UniversalEventType::SessionStarted {
|
||||
if self.session_started_emitted {
|
||||
return None;
|
||||
}
|
||||
self.session_started_emitted = true;
|
||||
}
|
||||
if conversion.event_type == UniversalEventType::SessionEnded
|
||||
&& agent_supports_resume(self.agent)
|
||||
&& !conversion.synthetic
|
||||
{
|
||||
return None;
|
||||
}
|
||||
if conversion.event_type == UniversalEventType::ItemStarted {
|
||||
if let UniversalEventData::Item(ref data) = conversion.data {
|
||||
if self.item_started.contains(&data.item.item_id) {
|
||||
|
|
@ -1463,6 +1478,11 @@ impl SessionManager {
|
|||
self.send_mock_message(session_id, message).await?;
|
||||
return Ok(());
|
||||
}
|
||||
if matches!(session_snapshot.agent, AgentId::Claude | AgentId::Amp) {
|
||||
let _ = self
|
||||
.record_conversions(&session_id, user_message_conversions(&message))
|
||||
.await;
|
||||
}
|
||||
if session_snapshot.agent == AgentId::Opencode {
|
||||
self.ensure_opencode_stream(session_id.clone()).await?;
|
||||
self.send_opencode_prompt(&session_snapshot, &message)
|
||||
|
|
@ -2067,15 +2087,17 @@ impl SessionManager {
|
|||
let status = tokio::task::spawn_blocking(move || child.wait()).await;
|
||||
match status {
|
||||
Ok(Ok(status)) if status.success() => {
|
||||
let message = format!("agent exited with status {:?}", status);
|
||||
self.mark_session_ended(
|
||||
&session_id,
|
||||
status.code(),
|
||||
&message,
|
||||
SessionEndReason::Completed,
|
||||
TerminatedBy::Agent,
|
||||
)
|
||||
.await;
|
||||
if !agent_supports_resume(agent) {
|
||||
let message = format!("agent exited with status {:?}", status);
|
||||
self.mark_session_ended(
|
||||
&session_id,
|
||||
status.code(),
|
||||
&message,
|
||||
SessionEndReason::Completed,
|
||||
TerminatedBy::Agent,
|
||||
)
|
||||
.await;
|
||||
}
|
||||
}
|
||||
Ok(Ok(status)) => {
|
||||
let message = format!("agent exited with status {:?}", status);
|
||||
|
|
@ -5035,6 +5057,46 @@ fn mock_user_message(prefix: &str, text: &str) -> Vec<EventConversion> {
|
|||
]
|
||||
}
|
||||
|
||||
fn user_message_conversions(text: &str) -> Vec<EventConversion> {
|
||||
let id = USER_MESSAGE_COUNTER.fetch_add(1, Ordering::Relaxed);
|
||||
let native_item_id = format!("user_{id}");
|
||||
let content = vec![ContentPart::Text {
|
||||
text: text.to_string(),
|
||||
}];
|
||||
vec![
|
||||
EventConversion::new(
|
||||
UniversalEventType::ItemStarted,
|
||||
UniversalEventData::Item(ItemEventData {
|
||||
item: UniversalItem {
|
||||
item_id: String::new(),
|
||||
native_item_id: Some(native_item_id.clone()),
|
||||
parent_id: None,
|
||||
kind: ItemKind::Message,
|
||||
role: Some(ItemRole::User),
|
||||
content: content.clone(),
|
||||
status: ItemStatus::InProgress,
|
||||
},
|
||||
}),
|
||||
)
|
||||
.synthetic(),
|
||||
EventConversion::new(
|
||||
UniversalEventType::ItemCompleted,
|
||||
UniversalEventData::Item(ItemEventData {
|
||||
item: UniversalItem {
|
||||
item_id: String::new(),
|
||||
native_item_id: Some(native_item_id),
|
||||
parent_id: None,
|
||||
kind: ItemKind::Message,
|
||||
role: Some(ItemRole::User),
|
||||
content,
|
||||
status: ItemStatus::Completed,
|
||||
},
|
||||
}),
|
||||
)
|
||||
.synthetic(),
|
||||
]
|
||||
}
|
||||
|
||||
fn mock_assistant_message(native_item_id: String, text: String) -> Vec<EventConversion> {
|
||||
let content = vec![ContentPart::Text { text }];
|
||||
vec![
|
||||
|
|
|
|||
|
|
@ -6,23 +6,19 @@ expression: value
|
|||
seq: 1
|
||||
session: started
|
||||
type: session.started
|
||||
- metadata: true
|
||||
seq: 2
|
||||
session: started
|
||||
type: session.started
|
||||
- item:
|
||||
content_types:
|
||||
- text
|
||||
kind: message
|
||||
role: user
|
||||
status: in_progress
|
||||
seq: 3
|
||||
seq: 2
|
||||
type: item.started
|
||||
- delta:
|
||||
delta: "<redacted>"
|
||||
item_id: "<redacted>"
|
||||
native_item_id: "<redacted>"
|
||||
seq: 4
|
||||
seq: 3
|
||||
type: item.delta
|
||||
- item:
|
||||
content_types:
|
||||
|
|
@ -30,11 +26,11 @@ expression: value
|
|||
kind: message
|
||||
role: user
|
||||
status: completed
|
||||
seq: 5
|
||||
seq: 4
|
||||
type: item.completed
|
||||
- permission:
|
||||
action: command_execution
|
||||
id: "<redacted>"
|
||||
status: requested
|
||||
seq: 6
|
||||
seq: 5
|
||||
type: permission.requested
|
||||
|
|
|
|||
|
|
@ -6,23 +6,19 @@ expression: value
|
|||
seq: 1
|
||||
session: started
|
||||
type: session.started
|
||||
- metadata: true
|
||||
seq: 2
|
||||
session: started
|
||||
type: session.started
|
||||
- item:
|
||||
content_types:
|
||||
- text
|
||||
kind: message
|
||||
role: user
|
||||
status: in_progress
|
||||
seq: 3
|
||||
seq: 2
|
||||
type: item.started
|
||||
- delta:
|
||||
delta: "<redacted>"
|
||||
item_id: "<redacted>"
|
||||
native_item_id: "<redacted>"
|
||||
seq: 4
|
||||
seq: 3
|
||||
type: item.delta
|
||||
- item:
|
||||
content_types:
|
||||
|
|
@ -30,11 +26,11 @@ expression: value
|
|||
kind: message
|
||||
role: user
|
||||
status: completed
|
||||
seq: 5
|
||||
seq: 4
|
||||
type: item.completed
|
||||
- question:
|
||||
id: "<redacted>"
|
||||
options: 2
|
||||
status: requested
|
||||
seq: 6
|
||||
seq: 5
|
||||
type: question.requested
|
||||
|
|
|
|||
|
|
@ -6,23 +6,19 @@ expression: value
|
|||
seq: 1
|
||||
session: started
|
||||
type: session.started
|
||||
- metadata: true
|
||||
seq: 2
|
||||
session: started
|
||||
type: session.started
|
||||
- item:
|
||||
content_types:
|
||||
- text
|
||||
kind: message
|
||||
role: user
|
||||
status: in_progress
|
||||
seq: 3
|
||||
seq: 2
|
||||
type: item.started
|
||||
- delta:
|
||||
delta: "<redacted>"
|
||||
item_id: "<redacted>"
|
||||
native_item_id: "<redacted>"
|
||||
seq: 4
|
||||
seq: 3
|
||||
type: item.delta
|
||||
- item:
|
||||
content_types:
|
||||
|
|
@ -30,11 +26,11 @@ expression: value
|
|||
kind: message
|
||||
role: user
|
||||
status: completed
|
||||
seq: 5
|
||||
seq: 4
|
||||
type: item.completed
|
||||
- question:
|
||||
id: "<redacted>"
|
||||
options: 2
|
||||
status: requested
|
||||
seq: 6
|
||||
seq: 5
|
||||
type: question.requested
|
||||
|
|
|
|||
|
|
@ -7,23 +7,19 @@ session_a:
|
|||
seq: 1
|
||||
session: started
|
||||
type: session.started
|
||||
- metadata: true
|
||||
seq: 2
|
||||
session: started
|
||||
type: session.started
|
||||
- item:
|
||||
content_types:
|
||||
- text
|
||||
kind: message
|
||||
role: user
|
||||
status: in_progress
|
||||
seq: 3
|
||||
seq: 2
|
||||
type: item.started
|
||||
- delta:
|
||||
delta: "<redacted>"
|
||||
item_id: "<redacted>"
|
||||
native_item_id: "<redacted>"
|
||||
seq: 4
|
||||
seq: 3
|
||||
type: item.delta
|
||||
- item:
|
||||
content_types:
|
||||
|
|
@ -31,7 +27,7 @@ session_a:
|
|||
kind: message
|
||||
role: user
|
||||
status: completed
|
||||
seq: 5
|
||||
seq: 4
|
||||
type: item.completed
|
||||
- item:
|
||||
content_types:
|
||||
|
|
@ -39,13 +35,13 @@ session_a:
|
|||
kind: message
|
||||
role: assistant
|
||||
status: in_progress
|
||||
seq: 6
|
||||
seq: 5
|
||||
type: item.started
|
||||
- delta:
|
||||
delta: "<redacted>"
|
||||
item_id: "<redacted>"
|
||||
native_item_id: "<redacted>"
|
||||
seq: 7
|
||||
seq: 6
|
||||
type: item.delta
|
||||
- item:
|
||||
content_types:
|
||||
|
|
@ -53,30 +49,26 @@ session_a:
|
|||
kind: message
|
||||
role: assistant
|
||||
status: completed
|
||||
seq: 8
|
||||
seq: 7
|
||||
type: item.completed
|
||||
session_b:
|
||||
- metadata: true
|
||||
seq: 1
|
||||
session: started
|
||||
type: session.started
|
||||
- metadata: true
|
||||
- item:
|
||||
content_types:
|
||||
- text
|
||||
kind: message
|
||||
role: user
|
||||
status: in_progress
|
||||
seq: 2
|
||||
session: started
|
||||
type: session.started
|
||||
- item:
|
||||
content_types:
|
||||
- text
|
||||
kind: message
|
||||
role: user
|
||||
status: in_progress
|
||||
type: item.started
|
||||
- delta:
|
||||
delta: "<redacted>"
|
||||
item_id: "<redacted>"
|
||||
native_item_id: "<redacted>"
|
||||
seq: 3
|
||||
type: item.started
|
||||
- delta:
|
||||
delta: "<redacted>"
|
||||
item_id: "<redacted>"
|
||||
native_item_id: "<redacted>"
|
||||
seq: 4
|
||||
type: item.delta
|
||||
- item:
|
||||
content_types:
|
||||
|
|
@ -84,7 +76,7 @@ session_b:
|
|||
kind: message
|
||||
role: user
|
||||
status: completed
|
||||
seq: 5
|
||||
seq: 4
|
||||
type: item.completed
|
||||
- item:
|
||||
content_types:
|
||||
|
|
@ -92,13 +84,13 @@ session_b:
|
|||
kind: message
|
||||
role: assistant
|
||||
status: in_progress
|
||||
seq: 6
|
||||
seq: 5
|
||||
type: item.started
|
||||
- delta:
|
||||
delta: "<redacted>"
|
||||
item_id: "<redacted>"
|
||||
native_item_id: "<redacted>"
|
||||
seq: 7
|
||||
seq: 6
|
||||
type: item.delta
|
||||
- item:
|
||||
content_types:
|
||||
|
|
@ -106,5 +98,5 @@ session_b:
|
|||
kind: message
|
||||
role: assistant
|
||||
status: completed
|
||||
seq: 8
|
||||
seq: 7
|
||||
type: item.completed
|
||||
|
|
|
|||
|
|
@ -6,23 +6,19 @@ expression: normalized
|
|||
seq: 1
|
||||
session: started
|
||||
type: session.started
|
||||
- metadata: true
|
||||
- item:
|
||||
content_types:
|
||||
- text
|
||||
kind: message
|
||||
role: user
|
||||
status: in_progress
|
||||
seq: 2
|
||||
session: started
|
||||
type: session.started
|
||||
- item:
|
||||
content_types:
|
||||
- text
|
||||
kind: message
|
||||
role: user
|
||||
status: in_progress
|
||||
type: item.started
|
||||
- delta:
|
||||
delta: "<redacted>"
|
||||
item_id: "<redacted>"
|
||||
native_item_id: "<redacted>"
|
||||
seq: 3
|
||||
type: item.started
|
||||
- delta:
|
||||
delta: "<redacted>"
|
||||
item_id: "<redacted>"
|
||||
native_item_id: "<redacted>"
|
||||
seq: 4
|
||||
type: item.delta
|
||||
- item:
|
||||
content_types:
|
||||
|
|
@ -30,7 +26,7 @@ expression: normalized
|
|||
kind: message
|
||||
role: user
|
||||
status: completed
|
||||
seq: 5
|
||||
seq: 4
|
||||
type: item.completed
|
||||
- item:
|
||||
content_types:
|
||||
|
|
@ -38,13 +34,13 @@ expression: normalized
|
|||
kind: message
|
||||
role: assistant
|
||||
status: in_progress
|
||||
seq: 6
|
||||
seq: 5
|
||||
type: item.started
|
||||
- delta:
|
||||
delta: "<redacted>"
|
||||
item_id: "<redacted>"
|
||||
native_item_id: "<redacted>"
|
||||
seq: 7
|
||||
seq: 6
|
||||
type: item.delta
|
||||
- item:
|
||||
content_types:
|
||||
|
|
@ -52,5 +48,5 @@ expression: normalized
|
|||
kind: message
|
||||
role: assistant
|
||||
status: completed
|
||||
seq: 8
|
||||
seq: 7
|
||||
type: item.completed
|
||||
|
|
|
|||
|
|
@ -6,23 +6,19 @@ expression: normalized
|
|||
seq: 1
|
||||
session: started
|
||||
type: session.started
|
||||
- metadata: true
|
||||
- item:
|
||||
content_types:
|
||||
- text
|
||||
kind: message
|
||||
role: user
|
||||
status: in_progress
|
||||
seq: 2
|
||||
session: started
|
||||
type: session.started
|
||||
- item:
|
||||
content_types:
|
||||
- text
|
||||
kind: message
|
||||
role: user
|
||||
status: in_progress
|
||||
type: item.started
|
||||
- delta:
|
||||
delta: "<redacted>"
|
||||
item_id: "<redacted>"
|
||||
native_item_id: "<redacted>"
|
||||
seq: 3
|
||||
type: item.started
|
||||
- delta:
|
||||
delta: "<redacted>"
|
||||
item_id: "<redacted>"
|
||||
native_item_id: "<redacted>"
|
||||
seq: 4
|
||||
type: item.delta
|
||||
- item:
|
||||
content_types:
|
||||
|
|
@ -30,7 +26,7 @@ expression: normalized
|
|||
kind: message
|
||||
role: user
|
||||
status: completed
|
||||
seq: 5
|
||||
seq: 4
|
||||
type: item.completed
|
||||
- item:
|
||||
content_types:
|
||||
|
|
@ -38,13 +34,13 @@ expression: normalized
|
|||
kind: message
|
||||
role: assistant
|
||||
status: in_progress
|
||||
seq: 6
|
||||
seq: 5
|
||||
type: item.started
|
||||
- delta:
|
||||
delta: "<redacted>"
|
||||
item_id: "<redacted>"
|
||||
native_item_id: "<redacted>"
|
||||
seq: 7
|
||||
seq: 6
|
||||
type: item.delta
|
||||
- item:
|
||||
content_types:
|
||||
|
|
@ -52,5 +48,5 @@ expression: normalized
|
|||
kind: message
|
||||
role: assistant
|
||||
status: completed
|
||||
seq: 8
|
||||
seq: 7
|
||||
type: item.completed
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue