use std::collections::BTreeSet;
use super::activity::unrepresented_runtime_work;
use super::adapters::{ARTIFACT_ADAPTERS, VISUAL_ADAPTERS, json_panel, render_tool_contribution};
use super::composer::composer;
use super::interactions::interaction_request;
use super::navigation::session_navigation;
use super::permissions::{permission_history, permission_request};
use super::tools::{render_tool_result, render_tool_result_with_context};
use super::transcript::{
is_active_interaction_summary, item_label, message_content, should_render_transcript_item,
transcript_item, transcript_item_body,
};
use super::usage::runtime_usage;
use super::*;
#[test]
fn queued_blocker_outside_transcript_exposes_only_shared_available_controls() {
use bcode_session_models::*;
let session_id = SessionId::new();
let id = SubmissionId("oldest".to_owned());
let mut snapshot = SessionViewSnapshot::empty();
snapshot.session_id = Some(session_id);
snapshot.runtime.submission_queue = Some(SubmissionQueueStatus {
oldest: Some(SubmissionRecord {
receipt: SubmissionReceipt {
submission_id: id.clone(),
accepted_event_sequence: 1,
},
submission: PromptSubmission {
id,
client_id: ClientId::new(),
text: "private prompt not rendered".to_owned(),
admission: TurnAdmissionMetadata::default(),
requested_placement: SubmissionPlacement::FollowUp,
target: SubmissionTarget::OwnTurn {
turn_id: TurnId("target".to_owned()),
},
execution_descriptor: None,
},
status: SubmissionStatus::Pending,
context_observation: None,
recovery_authorization: None,
}),
blocker: Some(SubmissionQueueBlocker::MissingContext),
});
let context = crate::context::tests::GuardedTestContext;
let rendered = home_content(&snapshot, &[], &context);
let mut text = String::new();
for container in &rendered {
container_text(container, &mut text);
}
assert!(text.contains("Queued message needs attention"));
assert!(text.contains("Review settings and resume"));
assert!(text.contains("Cancel this queued message"));
assert!(!text.contains("private prompt not rendered"));
assert!(format!("{rendered:?}").contains("prepare-submission-resume?test-capability=opaque"));
snapshot.runtime.submission_queue.as_mut().unwrap().blocker =
Some(SubmissionQueueBlocker::WorkflowAuthorityRequired);
let rendered = home_content(&snapshot, &[], &context);
let mut text = String::new();
for container in &rendered {
container_text(container, &mut text);
}
assert!(text.contains("workflow"));
assert!(!text.contains("Review settings and resume"));
assert!(!text.contains("Cancel this queued message"));
}
use bcode_session_models::{RuntimeWorkStatus, ToolArtifact, WorkId};
use bcode_session_view_models::{
ChatMessageView, CompactionView, CompactionViewStatus, InteractionViewSummary,
PermissionBatchView, PermissionView, RuntimeWorkView, SessionViewSnapshot, SkillView,
SkillViewStatus, TextFormat, ToolArtifactView, ToolInvocationView, ToolInvocationViewStatus,
ToolPresentationView, ToolResultView, ToolTimingView, TranscriptViewItem, TranscriptViewItemId,
TranscriptViewItemKind,
};
fn container_text(container: &hyperchad_transformer::Container, text: &mut String) {
if let hyperchad_transformer::Element::Text { value } = &container.element {
text.push_str(value);
}
for child in &container.children {
container_text(child, text);
}
}
fn transcript_fixture_item(
id: &str,
streaming: bool,
kind: TranscriptViewItemKind,
) -> TranscriptViewItem {
TranscriptViewItem {
output_location: None,
id: TranscriptViewItemId::new(id),
revision: 7,
sequence: Some(7),
timestamp_ms: Some(1_700_000_000_000),
streaming,
kind,
}
}
fn non_tool_message_fixture_items() -> Vec<TranscriptViewItem> {
vec![
transcript_fixture_item(
"fixture-user",
false,
TranscriptViewItemKind::UserMessage {
message: ChatMessageView::plain("user fixture"),
},
),
transcript_fixture_item(
"fixture-assistant",
true,
TranscriptViewItemKind::AssistantMessage {
message: ChatMessageView::markdown("**assistant fixture**"),
},
),
transcript_fixture_item(
"fixture-reasoning",
true,
TranscriptViewItemKind::ReasoningMessage {
message: ChatMessageView::markdown("reasoning fixture"),
},
),
transcript_fixture_item(
"fixture-system",
false,
TranscriptViewItemKind::SystemMessage {
message: ChatMessageView::plain("system fixture"),
},
),
]
}
fn non_tool_status_fixture_items() -> Vec<TranscriptViewItem> {
Vec::new()
}
fn non_tool_notice_fixture_items() -> Vec<TranscriptViewItem> {
let mut items = Vec::new();
for (index, status) in [
SkillViewStatus::Invoked,
SkillViewStatus::Suggested,
SkillViewStatus::ContextLoaded,
SkillViewStatus::Failed,
]
.into_iter()
.enumerate()
{
items.push(transcript_fixture_item(
&format!("fixture-skill-{index}"),
false,
TranscriptViewItemKind::Skill {
skill: SkillView {
skill_id: format!("skill-{index}"),
status,
text: format!("skill {status:?}"),
},
},
));
}
for (index, status) in [CompactionViewStatus::Local, CompactionViewStatus::Provider]
.into_iter()
.enumerate()
{
items.push(transcript_fixture_item(
&format!("fixture-compaction-{index}"),
false,
TranscriptViewItemKind::Compaction {
compaction: CompactionView {
status,
text: format!("compaction {status:?}"),
provider_plugin_id: (status == CompactionViewStatus::Provider)
.then(|| "provider.fixture".to_owned()),
model_id: (status == CompactionViewStatus::Provider)
.then(|| "model-fixture".to_owned()),
},
},
));
}
items
}
fn container_text_all(containers: &hyperchad::template::Containers) -> String {
let mut text = String::new();
for container in containers {
container_text(container, &mut text);
}
text
}
fn collect_resource_urls(container: &hyperchad_transformer::Container, urls: &mut Vec<String>) {
match &container.element {
hyperchad_transformer::Element::Anchor {
href: Some(href), ..
} => urls.push(href.clone()),
hyperchad_transformer::Element::Image {
source: Some(source),
..
} => urls.push(source.clone()),
_ => {}
}
for child in &container.children {
collect_resource_urls(child, urls);
}
}
fn exact_context_occupancy(tokens: u64) -> bcode_session_models::RequestContextOccupancy {
bcode_session_models::RequestContextOccupancy {
context_epoch: 0,
observation_sequence: 41,
observation: bcode_session_models::RequestContextObservation {
request: bcode_session_models::ModelRequestIdentity {
provider_plugin_id: "provider.example".to_owned(),
requested_model_id: None,
effective_model_id: "model-example".to_owned(),
request_id: "request".to_owned(),
model_turn_id: "turn-1".to_owned(),
round: 0,
request_fingerprint: "fingerprint".to_owned(),
effective_auth_profile: None,
context_format_version: None,
compatibility_key: None,
context_epoch: 0,
},
context_through_sequence: 40,
context_tokens: bcode_session_models::RequestContextTokenCount::ProviderExact(tokens),
local_estimate: bcode_session_models::LocalContextEstimate {
tokens,
algorithm_version: 1,
},
},
}
}
fn non_tool_permission_fixture_items(
session_id: bcode_session_models::SessionId,
) -> Vec<TranscriptViewItem> {
let permission = |id: &str, resolved: bool, approved| PermissionView {
permission_id: id.to_owned(),
session_id: Some(session_id),
tool_call_id: format!("call-{id}"),
tool_name: "fixture.tool".to_owned(),
arguments_json: "{}".to_owned(),
batch: None,
agent_id: "build".to_owned(),
title: Some("Fixture permission".to_owned()),
policy_source: None,
detail: Some(format!("permission {id}")),
resolved,
approved,
can_remember: true,
};
[
("requested", false, None),
("approved", true, Some(true)),
("denied", true, Some(false)),
]
.into_iter()
.map(|(id, resolved, approved)| {
transcript_fixture_item(
&format!("fixture-permission-{id}"),
false,
TranscriptViewItemKind::Permission {
permission: permission(id, resolved, approved),
},
)
})
.collect()
}
fn non_tool_interaction_fixture_items() -> Vec<TranscriptViewItem> {
let interaction = |id: &str, resolved: bool, resolution| InteractionViewSummary {
producer_id: None,
exchange_schema: None,
exchange_schema_version: None,
interaction_id: id.to_owned(),
kind: "fixture.interaction".to_owned(),
tool_call_id: Some(format!("call-{id}")),
title: Some(format!("Interaction {id}")),
required: true,
snapshot: Some(serde_json::json!({"state": "pending"})),
state: bcode_session_view_models::InteractionViewState::Pending,
status_detail: None,
resolved,
resolution,
};
[
interaction("pending", false, None),
interaction(
"resolved",
true,
Some(bcode_session_models::ToolExchangeResolution::Responded {
payload: serde_json::json!({"status": "answered"}),
}),
),
interaction(
"cancelled",
true,
Some(bcode_session_models::ToolExchangeResolution::Cancelled),
),
]
.into_iter()
.map(|interaction| {
let id = interaction.interaction_id.clone();
transcript_fixture_item(
&format!("fixture-interaction-{id}"),
false,
TranscriptViewItemKind::Interaction { interaction },
)
})
.collect()
}
fn non_tool_usage_and_plugin_fixture_items() -> Vec<TranscriptViewItem> {
Vec::new()
}
fn tool_fixture(status: ToolInvocationViewStatus) -> ToolInvocationView {
let terminal = matches!(
status,
ToolInvocationViewStatus::Finished
| ToolInvocationViewStatus::Failed
| ToolInvocationViewStatus::Cancelled
);
ToolInvocationView {
tool_call_id: format!("fixture-tool-{status:?}"),
producer_plugin_id: Some("fixture-plugin".to_owned()),
tool_name: Some("fixture.tool".to_owned()),
arguments_json: Some(r#"{"path":"/tmp/fixture"}"#.to_owned()),
working_directory: Some("/tmp".into()),
request_draft: None,
status,
result_text: terminal.then(|| "fixture result".to_owned()),
is_error: Some(status == ToolInvocationViewStatus::Failed),
result: None,
presentation: None,
timing: ToolTimingView {
started_at_ms: Some(1_000),
finished_at_ms: terminal.then_some(2_250),
timeout_ms: None,
timed_out: None,
duration_ms: terminal.then_some(1_250),
},
}
}
#[test]
fn every_tool_transcript_kind_and_lifecycle_state_renders() {
for status in [
ToolInvocationViewStatus::Requested,
ToolInvocationViewStatus::Running,
ToolInvocationViewStatus::Waiting,
ToolInvocationViewStatus::Finished,
ToolInvocationViewStatus::Failed,
ToolInvocationViewStatus::Cancelled,
] {
let lifecycle = transcript_fixture_item(
&format!("fixture-tool-lifecycle-{status:?}"),
matches!(
status,
ToolInvocationViewStatus::Running | ToolInvocationViewStatus::Waiting
),
TranscriptViewItemKind::ToolInvocation {
tool: Box::new(tool_fixture(status)),
},
);
let rendered = container_text_all(&transcript_item(&lifecycle));
assert!(rendered.contains("fixture.tool"));
assert!(rendered.contains(match status {
ToolInvocationViewStatus::Requested => "requested",
ToolInvocationViewStatus::Running => "running",
ToolInvocationViewStatus::Waiting => "waiting",
ToolInvocationViewStatus::Finished => "finished",
ToolInvocationViewStatus::Failed => "failed",
ToolInvocationViewStatus::Cancelled => "cancelled",
}));
}
let request = transcript_fixture_item(
"fixture-tool-request",
false,
TranscriptViewItemKind::ToolRequest {
tool: Box::new(tool_fixture(ToolInvocationViewStatus::Requested)),
},
);
let rendered = container_text_all(&transcript_item(&request));
assert!(rendered.contains("fixture.tool"));
assert!(rendered.contains("requested"));
}
#[test]
fn every_non_tool_transcript_state_survives_reconnect_snapshot_and_renders() {
let session_id = bcode_session_models::SessionId::new();
let mut items = non_tool_message_fixture_items();
items.extend(non_tool_status_fixture_items());
items.extend(non_tool_notice_fixture_items());
items.extend(non_tool_permission_fixture_items(session_id));
items.extend(non_tool_interaction_fixture_items());
items.extend(non_tool_usage_and_plugin_fixture_items());
let mut snapshot = SessionViewSnapshot::empty();
snapshot.session_id = Some(session_id);
snapshot.transcript.revision = 7;
snapshot.transcript.source_start_sequence = Some(7);
snapshot.transcript.source_end_sequence = Some(7);
snapshot.transcript.items = items;
let encoded = serde_json::to_vec(&snapshot).expect("serialize reconnect snapshot");
let reconnected: SessionViewSnapshot =
serde_json::from_slice(&encoded).expect("deserialize reconnect snapshot");
assert_eq!(reconnected, snapshot);
let rendered = format!(
"{:?}",
home(
&reconnected,
&[],
&crate::context::StaticPresentationContext
)
);
let text = reconnected
.transcript
.items
.iter()
.map(|item| container_text_all(&transcript_item(item)))
.collect::<String>();
assert!(rendered.contains(&semantic_dom_id("transcript-item", "fixture-assistant")));
assert!(text.contains("live"));
for expected in [
"user fixture",
"assistant fixture",
"reasoning fixture",
"system fixture",
"Failed",
"skill Invoked",
"skill Suggested",
"skill ContextLoaded",
"skill Failed",
"Local context compacted",
"Provider context compacted",
"provider.fixture",
"model-fixture",
"requested",
"approved",
"denied",
"pending",
"answered",
"cancelled",
] {
assert!(
text.contains(expected),
"missing rendered state: {expected}"
);
}
}
#[test]
fn semantic_component_identities_are_stable_bounded_and_order_independent() {
let long = "plugin-controlled-id".repeat(10_000);
let first = semantic_dom_id("component", &long);
let repeated = semantic_dom_id("component", &long);
let different = semantic_dom_id("component", "different");
assert_eq!(first, repeated);
assert_ne!(first, different);
assert!(first.len() < 64);
assert!(
first
.chars()
.all(|character| character.is_ascii_alphanumeric() || character == '-')
);
let session_a = bcode_session_models::SessionId::new();
let session_b = bcode_session_models::SessionId::new();
let sessions = |reverse: bool| {
let mut values = [session_a, session_b]
.into_iter()
.map(|id| bcode_session_models::SessionSummary {
id,
name: Some(format!("session {id}")),
explicit_name: None,
derived_title: None,
automatic_title: None,
automatic_title_source_sequence: None,
title_source: bcode_session_models::SessionTitleSource::Explicit,
client_count: 0,
created_at_ms: 1,
updated_at_ms: 1,
working_directory: "/tmp".into(),
import: None,
execution: None,
parent_id: None,
location: None,
})
.collect::<Vec<_>>();
if reverse {
values.reverse();
}
values
};
for reverse in [false, true] {
let rendered = format!(
"{:?}",
session_navigation(
&sessions(reverse),
None,
&bcode_session_view_models::SessionCatalogViewStatus::Loaded,
&crate::context::StaticPresentationContext,
)
);
for id in [session_a, session_b] {
assert!(rendered.contains(&semantic_dom_id("session", &id.to_string())));
}
}
}
#[test]
fn repeated_domain_components_use_stable_semantic_identities() {
let session_id = bcode_session_models::SessionId::new();
let permission_id = "stable-permission";
let interaction_id = "stable-interaction";
let work_id = "stable-work";
let transcript_id = "stable-transcript";
let mut snapshot = SessionViewSnapshot::empty();
snapshot.session_id = Some(session_id);
snapshot.permissions.push(PermissionView {
permission_id: permission_id.to_owned(),
session_id: Some(session_id),
tool_call_id: "call-permission".to_owned(),
tool_name: "fixture.tool".to_owned(),
arguments_json: "{}".to_owned(),
batch: None,
agent_id: "build".to_owned(),
title: Some("Permission".to_owned()),
policy_source: None,
detail: None,
resolved: false,
approved: None,
can_remember: false,
});
snapshot.interactions.push(InteractionViewSummary {
producer_id: None,
exchange_schema: None,
exchange_schema_version: None,
interaction_id: interaction_id.to_owned(),
kind: "future.interaction".to_owned(),
tool_call_id: None,
title: Some("Interaction".to_owned()),
required: false,
snapshot: None,
state: bcode_session_view_models::InteractionViewState::Pending,
status_detail: None,
resolved: false,
resolution: None,
});
snapshot.runtime_work.push(RuntimeWorkView {
work_id: WorkId::new(work_id),
kind: bcode_session_models::RuntimeWorkKind::Tool,
label: "Work".to_owned(),
status: RuntimeWorkStatus::Running,
cancellable: false,
message: None,
completed_units: None,
total_units: None,
updated_at_ms: None,
});
snapshot.transcript.items.push(transcript_fixture_item(
transcript_id,
false,
TranscriptViewItemKind::SystemMessage {
message: ChatMessageView::plain("System"),
},
));
let expected = [
semantic_dom_id("permission", permission_id),
semantic_dom_id("interaction", interaction_id),
semantic_dom_id("runtime-work", work_id),
semantic_dom_id("transcript-item", transcript_id),
];
for revision in [1, 2] {
snapshot.revision = revision;
snapshot.runtime_work.reverse();
snapshot.permissions.reverse();
let rendered = format!(
"{:?}",
home(&snapshot, &[], &crate::context::StaticPresentationContext)
);
for id in &expected {
assert!(rendered.contains(id));
}
for static_id in [
"bcode-web-shell",
"session-navigation",
"conversation-timeline",
"composer-region",
] {
assert!(rendered.contains(static_id));
}
}
}
#[test]
fn usage_and_context_render_compact_summaries_with_secondary_details() {
let usage = bcode_session_models::SessionTokenUsage {
input_tokens: Some(100),
output_tokens: Some(25),
total_tokens: Some(125),
cached_input_tokens: Some(40),
cache_write_input_tokens: Some(10),
reasoning_tokens: Some(5),
..bcode_session_models::SessionTokenUsage::default()
};
let runtime = bcode_session_view_models::SessionRuntimeViewState {
context_occupancy: Some(exact_context_occupancy(8_000)),
cumulative_metered_tokens: 12_345,
latest_usage: Some(usage),
..bcode_session_view_models::SessionRuntimeViewState::default()
};
let runtime_text = container_text_all(&runtime_usage(&runtime));
assert!(runtime_text.contains("current context"));
assert!(runtime_text.contains("8000 tokens"));
assert!(runtime_text.contains("measured"));
assert!(runtime_text.contains("12345 tokens"));
assert!(runtime_text.contains("usage details"));
assert!(runtime_text.contains("cached input"));
}
#[test]
fn empty_and_long_messages_render_bounded_explicit_states() {
let empty = ChatMessageView::plain("");
let long = ChatMessageView::plain("x".repeat(100_100));
let empty_containers = message_content(&empty);
let long_containers = message_content(&long);
let mut empty_text = String::new();
let mut long_text = String::new();
for container in &empty_containers {
container_text(container, &mut empty_text);
}
for container in &long_containers {
container_text(container, &mut long_text);
}
assert_eq!(empty_text, "Empty message");
assert!(long_text.contains("Message truncated for display."));
assert_eq!(long_text.matches('x').count(), 100_000);
}
#[test]
fn malformed_markdown_remains_safe_and_readable() {
let malformed = ChatMessageView::markdown("[broken](<\n\n```rust\nfn unfinished() {");
let containers = message_content(&malformed);
let mut text = String::new();
for container in &containers {
container_text(container, &mut text);
}
assert!(text.contains("broken"));
assert!(text.contains("fn unfinished()"));
}
#[test]
fn markdown_messages_use_hyperchad_markdown_with_highlighting_and_xss_protection() {
let options = hyperchad::markdown::MarkdownOptions::default();
assert!(options.syntax_highlighting);
assert!(options.xss_protection);
let message = ChatMessageView {
text: "# Heading\n\n```rust\nfn main() {}\n```\n\n<script>alert('unsafe')</script>\n\n[unsafe link](javascript:alert(2))\n\n[safe link](https://example.com)"
.to_owned(),
display_label: None,
activity: None,
submission_status: None,
submission_context: None,
format: TextFormat::Markdown,
};
let containers = message_content(&message);
let mut text = String::new();
for container in &containers {
container_text(container, &mut text);
}
let rendered = format!("{containers:?}");
assert!(text.contains("Heading"));
assert!(text.contains("fn main() {}"));
assert!(rendered.contains("markdown"));
assert!(!rendered.contains("Element::Raw"));
assert!(!text.contains("alert('unsafe')"));
assert!(!rendered.contains("javascript:alert"));
assert!(rendered.contains("https://example.com"));
}
#[test]
fn activity_messages_expose_bounded_details_without_parsing_payload_markup() {
let mut message = ChatMessageView::plain("Iteration preview");
message.activity = Some(Box::new(bcode_session_view_models::ActivityMessageView {
source_sequence: 42,
exact_instructions: Some("**exact instructions** <script>not markup</script>".into()),
instruction_bytes: 48,
execution: bcode_session_models::TurnExecutionCorrelation {
execution_id: "run-a".into(),
unit_id: "evaluate".into(),
attempt: 2,
},
presentation: bcode_session_models::ActivityPresentation {
version: bcode_session_models::ACTIVITY_PRESENTATION_VERSION,
producer: "example.plugin".into(),
activity_id: "iteration:1".into(),
revision: 1,
schema: "unknown.schema".into(),
schema_version: 1,
fallback: "Iteration preview".into(),
payload: serde_json::json!({"input":"<script>alert(1)</script>", "large": "x".repeat(40_000)}),
},
}));
let containers = message_content(&message);
let mut text = String::new();
for container in &containers {
container_text(container, &mut text);
}
assert!(text.contains("Activity details"));
assert!(text.contains("Canonical instructions"));
assert!(!text.contains("**exact instructions** <script>not markup</script>"));
assert!(text.contains("run-a"));
assert!(text.contains("session event 42"));
assert!(text.contains("not the exact submitted instructions"));
assert!(!text.contains("Structured details truncated for display."));
assert!(!format!("{containers:?}").contains("Element::Raw"));
assert!(!text.contains(&"x".repeat(40_000)));
message.activity = None;
let mut ordinary = String::new();
for container in &message_content(&message) {
container_text(container, &mut ordinary);
}
assert!(!ordinary.contains("Activity details"));
}
#[test]
fn plain_text_messages_do_not_parse_markdown() {
let message = ChatMessageView {
text: "**literal**".to_owned(),
display_label: None,
activity: None,
submission_status: None,
submission_context: None,
format: TextFormat::PlainText,
};
let rendered = format!("{:?}", message_content(&message));
assert!(rendered.contains("**literal**"));
assert!(!rendered.contains("markdown"));
}
#[test]
fn user_and_assistant_messages_have_distinct_semantic_surfaces() {
let user = TranscriptViewItem {
output_location: None,
id: TranscriptViewItemId::new("user:surface"),
sequence: Some(1),
timestamp_ms: None,
revision: 1,
streaming: false,
kind: TranscriptViewItemKind::UserMessage {
message: ChatMessageView::markdown("User message"),
},
};
let assistant = TranscriptViewItem {
output_location: None,
id: TranscriptViewItemId::new("assistant:surface"),
sequence: Some(2),
timestamp_ms: None,
revision: 1,
streaming: false,
kind: TranscriptViewItemKind::AssistantMessage {
message: ChatMessageView::markdown("Assistant message"),
},
};
let user = format!("{:?}", transcript_item(&user));
let assistant = format!("{:?}", transcript_item(&assistant));
assert!(user.contains(&semantic_dom_id("transcript-item", "user:surface")));
assert!(assistant.contains(&semantic_dom_id("transcript-item", "assistant:surface")));
assert!(user.contains("margin_left: Some(Integer(48))"));
assert!(assistant.contains("margin_right: Some(Integer(48))"));
assert_ne!(user, assistant);
}
#[test]
fn streaming_assistant_item_has_stable_identity_live_state_and_developer_metadata() {
let item = TranscriptViewItem {
output_location: None,
id: TranscriptViewItemId::new("assistant:stream"),
sequence: Some(41),
timestamp_ms: Some(42),
revision: 3,
streaming: true,
kind: TranscriptViewItemKind::AssistantMessage {
message: ChatMessageView::markdown("Streaming **answer**"),
},
};
let containers = transcript_item(&item);
let mut text = String::new();
for container in &containers {
container_text(container, &mut text);
}
let rendered = format!("{containers:?}");
assert!(rendered.contains(&semantic_dom_id("transcript-item", "assistant:stream")));
assert!(text.contains("live"));
assert!(text.contains("developer details"));
assert!(text.contains("revision 3"));
assert!(text.contains("event 41"));
}
#[test]
fn reasoning_item_uses_collapsed_semantic_disclosure() {
let item = TranscriptViewItem {
output_location: None,
id: TranscriptViewItemId::new("reasoning:details"),
sequence: Some(1),
timestamp_ms: None,
revision: 1,
streaming: false,
kind: TranscriptViewItemKind::ReasoningMessage {
message: ChatMessageView::markdown("Because **reasons**"),
},
};
let rendered = format!("{:?}", transcript_item(&item));
assert!(rendered.contains("Reasoning"));
assert!(rendered.contains("Details"));
assert!(!rendered.contains("open: true"));
}
#[test]
fn system_skill_and_compaction_items_render_as_notices() {
let system = TranscriptViewItemKind::SystemMessage {
message: ChatMessageView::plain("System notice"),
};
let skill = TranscriptViewItemKind::Skill {
skill: SkillView {
skill_id: "broken".to_owned(),
status: SkillViewStatus::Failed,
text: "Skill failed".to_owned(),
},
};
let compaction = TranscriptViewItemKind::Compaction {
compaction: CompactionView {
status: CompactionViewStatus::Local,
text: "Older context summarized".to_owned(),
provider_plugin_id: None,
model_id: None,
},
};
let system = format!("{:?}", transcript_item_body(&system));
let skill = format!("{:?}", transcript_item_body(&skill));
let compaction = format!("{:?}", transcript_item_body(&compaction));
assert!(system.contains("Aside"));
assert!(skill.contains("Aside"));
assert!(skill.contains("r: 248"));
assert!(skill.contains("g: 81"));
assert!(skill.contains("b: 73"));
assert!(compaction.contains("Local context compacted"));
}
#[test]
fn structured_reasoning_activity_renders_terminal_status_chrome() {
let sentinel = "encrypted-sentinel-do-not-expose";
for (status, expected) in [
(
bcode_session_models::ReasoningActivityStatus::Completed,
"Reasoning",
),
(
bcode_session_models::ReasoningActivityStatus::Interrupted,
"Reasoning interrupted",
),
(
bcode_session_models::ReasoningActivityStatus::Failed,
"Reasoning failed",
),
] {
let kind = TranscriptViewItemKind::ReasoningActivity {
activity: bcode_session_view_models::ReasoningActivityView {
turn_id: "turn-1".to_owned(),
activity_id: "reasoning-1".to_owned(),
order: 0,
status,
parts: Vec::new(),
opaque: true,
readable_parts_filtered: false,
finished: true,
},
};
let rendered = format!("{:?}", transcript_item_body(&kind));
assert!(rendered.contains(expected));
assert!(!rendered.contains("Empty message"));
assert!(!rendered.contains(sentinel));
assert!(
rendered.contains("did not return readable reasoning content"),
"withheld reasoning must be explained: {rendered}"
);
let mut kind = kind;
let TranscriptViewItemKind::ReasoningActivity { activity } = &mut kind else {
unreachable!();
};
activity.finished = false;
let pending = format!("{:?}", transcript_item_body(&kind));
assert!(!pending.contains("did not return readable reasoning content"));
}
}
#[test]
fn reasoning_activity_chrome_remains_visible_without_readable_text() {
let item = TranscriptViewItem {
output_location: None,
id: TranscriptViewItemId::new("reasoning:test"),
sequence: Some(1),
timestamp_ms: None,
revision: 1,
streaming: false,
kind: TranscriptViewItemKind::ReasoningMessage {
message: ChatMessageView::markdown(""),
},
};
assert!(should_render_transcript_item(&item));
let rendered = format!("{:?}", transcript_item_body(&item.kind));
assert!(rendered.contains("Reasoning"));
assert!(!rendered.contains("Empty message"));
}
#[test]
fn skill_transcript_item_renders_semantic_label_and_text() {
let item = TranscriptViewItem {
output_location: None,
id: TranscriptViewItemId::new("skill:test"),
sequence: Some(1),
timestamp_ms: None,
revision: 1,
streaming: false,
kind: TranscriptViewItemKind::Skill {
skill: SkillView {
skill_id: "review".to_owned(),
status: SkillViewStatus::Failed,
text: "review: boom".to_owned(),
},
},
};
assert_eq!(item_label(&item.kind), "skill error");
let rendered = format!("{:?}", transcript_item(&item));
assert!(rendered.contains("review: boom"));
let context_item = TranscriptViewItem {
output_location: None,
id: TranscriptViewItemId::new("skill:context"),
sequence: Some(2),
timestamp_ms: None,
revision: 1,
streaming: false,
kind: TranscriptViewItemKind::Skill {
skill: SkillView {
skill_id: "review".to_owned(),
status: SkillViewStatus::ContextLoaded,
text: "loaded review".to_owned(),
},
},
};
assert_eq!(item_label(&context_item.kind), "skill context");
let rendered = format!("{:?}", transcript_item(&context_item));
assert!(rendered.contains("loaded review"));
let compaction_item = TranscriptViewItem {
output_location: None,
id: TranscriptViewItemId::new("compaction:test"),
sequence: Some(3),
timestamp_ms: None,
revision: 1,
streaming: false,
kind: TranscriptViewItemKind::Compaction {
compaction: CompactionView {
status: CompactionViewStatus::Local,
text: "local context compaction: summary".to_owned(),
provider_plugin_id: None,
model_id: None,
},
},
};
assert_eq!(item_label(&compaction_item.kind), "compaction");
let rendered = format!("{:?}", transcript_item(&compaction_item));
assert!(rendered.contains("local context compaction: summary"));
}
#[test]
fn hyperchad_shell_renders_all_primary_regions_including_runtime_state() {
let mut snapshot = SessionViewSnapshot::empty();
snapshot.session_id = Some(bcode_session_models::SessionId::new());
snapshot.connection_status = bcode_session_view_models::SessionConnectionViewStatus::Attached;
snapshot.runtime_work.push(RuntimeWorkView {
work_id: WorkId::new("work-1"),
kind: bcode_session_models::RuntimeWorkKind::Tool,
label: "index workspace".to_owned(),
status: RuntimeWorkStatus::Running,
cancellable: true,
message: Some("indexing".to_owned()),
completed_units: Some(2),
total_units: Some(4),
updated_at_ms: Some(1),
});
let rendered = format!(
"{:?}",
home(&snapshot, &[], &crate::context::StaticPresentationContext)
);
assert!(rendered.contains("sessions"));
assert!(rendered.contains("transcript"));
assert!(rendered.contains("composer"));
assert!(rendered.contains("Connected · session attached"));
assert!(rendered.contains("runtime work"));
assert!(rendered.contains("index workspace"));
assert!(rendered.contains("work-1"));
}
#[test]
fn permission_controls_distinguish_single_batch_and_resolved_history() {
let session_id = bcode_session_models::SessionId::new();
let permission = |call_count, resolved, approved| PermissionView {
permission_id: "permission-1".to_owned(),
session_id: Some(session_id),
tool_call_id: "call-1".to_owned(),
tool_name: "shell.run".to_owned(),
arguments_json: r#"{"command":"pwd"}"#.to_owned(),
batch: Some(PermissionBatchView {
batch_id: "batch-1".to_owned(),
call_index: 0,
call_count,
}),
agent_id: "agent-1".to_owned(),
title: Some("Permission requested".to_owned()),
policy_source: Some("workspace policy".to_owned()),
detail: Some("Review command execution".to_owned()),
resolved,
approved,
can_remember: true,
};
let single_containers = permission_request(
&permission(1, false, None),
Some(session_id),
&crate::context::tests::GuardedTestContext,
);
let batch_containers = permission_request(
&permission(3, false, None),
Some(session_id),
&crate::context::tests::GuardedTestContext,
);
let resolved_containers = permission_history(&permission(3, true, Some(false)));
let mut single = String::new();
let mut batch = String::new();
let mut resolved = String::new();
for container in &single_containers {
container_text(container, &mut single);
}
for container in &batch_containers {
container_text(container, &mut batch);
}
for container in &resolved_containers {
container_text(container, &mut resolved);
}
let single_tree = format!("{single_containers:?}");
let batch_tree = format!("{batch_containers:?}");
let resolved_tree = format!("{resolved_containers:?}");
assert!(single.contains("shell.run"));
assert!(single.contains("agent-1"));
assert!(single.contains("workspace policy"));
assert!(single.contains("decision can be remembered"));
assert!(single.contains("remember"));
assert!(!single.contains("approve all"));
assert!(batch.contains("call"));
assert!(batch.contains("approve all 3"));
assert!(batch.contains("deny all 3"));
assert!(batch_tree.contains("/actions/permission-batch"));
assert!(single_tree.contains("/actions/permission?"));
assert!(resolved.contains("denied"));
assert!(!resolved_tree.contains("/actions/permission"));
assert!(!resolved.contains("approve"));
}
#[test]
fn grouped_permission_renders_per_call_and_apply_to_all_actions() {
let mut snapshot = SessionViewSnapshot::empty();
snapshot.session_id = Some(bcode_session_models::SessionId::new());
snapshot.permissions.push(PermissionView {
permission_id: "permission-1".to_owned(),
session_id: snapshot.session_id,
tool_call_id: "call-1".to_owned(),
tool_name: "shell.run".to_owned(),
arguments_json: r#"{"command":"pwd"}"#.to_owned(),
batch: Some(PermissionBatchView {
batch_id: "batch-1".to_owned(),
call_index: 0,
call_count: 3,
}),
agent_id: "agent-1".to_owned(),
title: Some("Permission requested".to_owned()),
policy_source: Some("test".to_owned()),
detail: Some("review".to_owned()),
resolved: false,
approved: None,
can_remember: true,
});
let rendered = format!(
"{:?}",
home(&snapshot, &[], &crate::context::tests::GuardedTestContext)
);
assert!(rendered.contains("batch"));
assert!(rendered.contains('1'));
assert!(rendered.contains('3'));
assert!(rendered.contains("approve all"));
assert!(rendered.contains("deny all"));
assert!(rendered.contains("/actions/permission-batch"));
assert!(rendered.contains("/actions/permission?"));
assert!(rendered.contains("batch-1"));
}
#[test]
fn transcript_history_controls_render_source_anchored_actions() {
let mut snapshot = SessionViewSnapshot::empty();
snapshot.session_id = Some(bcode_session_models::SessionId::new());
snapshot.transcript.source_start_sequence = Some(10);
snapshot.transcript.source_end_sequence = Some(20);
snapshot.transcript.has_older_history = true;
snapshot.transcript.has_newer_history = true;
let rendered = format!(
"{:?}",
home(&snapshot, &[], &crate::context::tests::GuardedTestContext)
);
assert!(rendered.contains("/actions/history-window?test-capability=opaque"));
assert!(rendered.contains("load older history"));
assert!(rendered.contains("load newer history"));
assert!(rendered.contains("10"));
assert!(rendered.contains("20"));
}
fn container_by_id<'a>(
containers: &'a [hyperchad_transformer::Container],
id: &str,
) -> Option<&'a hyperchad_transformer::Container> {
containers.iter().find_map(|container| {
(container.str_id.as_deref() == Some(id))
.then_some(container)
.or_else(|| container_by_id(&container.children, id))
})
}
fn responsive_targets(
container: &hyperchad_transformer::Container,
targets: &mut BTreeSet<String>,
) {
for config in &container.overrides {
let hyperchad_transformer::OverrideCondition::ResponsiveTarget { name } = &config.condition;
targets.insert(name.clone());
}
for child in &container.children {
responsive_targets(child, targets);
}
}
#[test]
fn application_shell_declares_canonical_tablet_and_narrow_layouts() {
let snapshot = SessionViewSnapshot::empty();
let containers = home(&snapshot, &[], &crate::context::StaticPresentationContext);
let mut targets = BTreeSet::new();
for container in &containers {
responsive_targets(container, &mut targets);
}
assert_eq!(
targets,
BTreeSet::from(["narrow".to_owned(), "tablet".to_owned()])
);
let main = container_by_id(&containers, "conversation-main").expect("conversation main");
assert_eq!(
main.max_width,
Some(hyperchad_transformer::Number::Integer(960))
);
let runtime = container_by_id(&containers, "runtime-summary").expect("runtime summary");
assert_eq!(
runtime.direction,
hyperchad_transformer_models::LayoutDirection::Row
);
assert!(runtime.overrides.iter().any(|config| {
matches!(
config.condition,
hyperchad_transformer::OverrideCondition::ResponsiveTarget { ref name }
if name == "narrow"
) && config.overrides.iter().any(|item| {
matches!(
item,
hyperchad_transformer::OverrideItem::Direction(
hyperchad_transformer_models::LayoutDirection::Column
)
)
})
}));
}
#[test]
fn session_navigation_marks_selected_active_and_idle_sessions() {
let selected = bcode_session_models::SessionSummary {
id: bcode_session_models::SessionId::new(),
name: Some("Selected session".to_owned()),
explicit_name: None,
derived_title: None,
automatic_title: None,
automatic_title_source_sequence: None,
title_source: bcode_session_models::SessionTitleSource::Explicit,
client_count: 2,
created_at_ms: 1,
updated_at_ms: 42,
working_directory: "/tmp/selected".into(),
import: None,
execution: None,
parent_id: None,
location: None,
};
let idle = bcode_session_models::SessionSummary {
id: bcode_session_models::SessionId::new(),
name: Some("Idle session".to_owned()),
client_count: 0,
working_directory: "/tmp/idle".into(),
..selected.clone()
};
let containers = session_navigation(
&[selected.clone(), idle],
Some(selected.id),
&bcode_session_view_models::SessionCatalogViewStatus::Loaded,
&crate::context::StaticPresentationContext,
);
let mut text = String::new();
for container in &containers {
container_text(container, &mut text);
}
assert!(text.contains("Selected session"));
assert!(text.contains("selected · active"));
assert!(text.contains("2 connected"));
assert!(text.contains("Idle session"));
assert!(text.contains("idle"));
}
#[test]
fn composer_presents_ready_disabled_and_message_placement_states() {
let mut ready = SessionViewSnapshot::empty();
ready.composer.can_submit = true;
ready.composer.draft = "Preserved draft".to_owned();
ready.session_id = Some(bcode_session_models::SessionId::new());
let session_id = ready.session_id.expect("composer session");
let mut pending = ready.clone();
pending.composer.can_submit = false;
pending.composer.disabled_reason = Some("Sending message".to_owned());
let mut errored = ready.clone();
errored.composer.can_submit = false;
errored.composer.disabled_reason = Some("Message submission failed".to_owned());
let mut disabled = ready.clone();
disabled.composer.can_submit = false;
disabled.composer.disabled_reason = Some("Wait for the active operation".to_owned());
let ready = format!(
"{:?}",
composer(&ready, &crate::context::tests::GuardedTestContext)
);
let pending = format!(
"{:?}",
composer(&pending, &crate::context::tests::GuardedTestContext)
);
let errored = format!(
"{:?}",
composer(&errored, &crate::context::tests::GuardedTestContext)
);
let disabled = format!(
"{:?}",
composer(&disabled, &crate::context::tests::GuardedTestContext)
);
assert!(ready.contains("Ready"));
assert!(ready.contains("Ready to send"));
assert!(ready.contains("Preserved draft"));
assert!(ready.contains("Steer the active turn"));
assert!(ready.contains("Queue as a follow-up"));
assert!(ready.contains("Send message"));
assert!(ready.contains("/actions/submit-message?test-capability=opaque"));
assert!(ready.contains(&format!(
"/actions/update-draft/{session_id}?test-capability=opaque"
)));
assert!(ready.contains("/actions/cancel-turn?test-capability=opaque"));
assert!(ready.contains("clear_queue"));
assert!(ready.contains("placement"));
assert!(ready.contains("composer-message-label"));
assert!(ready.contains("message-placement-label"));
assert!(pending.contains("Pending"));
assert!(pending.contains("Sending message"));
assert!(errored.contains("Error"));
assert!(errored.contains("Message submission failed"));
assert!(disabled.contains("Disabled"));
assert!(disabled.contains("Wait for the active operation"));
assert!(disabled.contains("Sending unavailable"));
assert!(disabled.contains("disabled"));
}
#[test]
fn opaque_action_context_is_propagated_to_all_forms() {
let rendered = format!(
"{:?}",
home(
&SessionViewSnapshot::empty(),
&[],
&crate::context::tests::GuardedTestContext
)
);
assert!(rendered.contains("/actions/submit-message?test-capability=opaque"));
}
#[test]
fn opaque_session_context_is_propagated_to_navigation() {
let session = bcode_session_models::SessionSummary {
id: bcode_session_models::SessionId::new(),
name: Some("session".to_owned()),
explicit_name: None,
derived_title: None,
automatic_title: None,
automatic_title_source_sequence: None,
title_source: bcode_session_models::SessionTitleSource::Explicit,
client_count: 0,
created_at_ms: 1,
updated_at_ms: 1,
working_directory: "/tmp/project".into(),
import: None,
execution: None,
parent_id: None,
location: None,
};
let rendered = format!(
"{:?}",
home(
&SessionViewSnapshot::empty(),
std::slice::from_ref(&session),
&crate::context::tests::GuardedTestContext
)
);
assert!(rendered.contains(&format!(
"test-capability=opaque&test-event-scope=session-{}",
session.id
)));
}
#[test]
fn unknown_interactions_keep_bounded_active_controls_and_resolved_history() {
let session_id = bcode_session_models::SessionId::new();
let active = InteractionViewSummary {
producer_id: None,
exchange_schema: None,
exchange_schema_version: None,
interaction_id: "interaction-unknown".to_owned(),
kind: "future.interaction".to_owned(),
tool_call_id: Some("call-unknown".to_owned()),
title: Some("Future interaction".to_owned()),
required: true,
snapshot: Some(serde_json::json!({"sentinel": "u".repeat(40_000)})),
state: bcode_session_view_models::InteractionViewState::Pending,
status_detail: None,
resolved: false,
resolution: None,
};
let cancelled = InteractionViewSummary {
state: bcode_session_view_models::InteractionViewState::Pending,
status_detail: None,
resolved: true,
resolution: Some(bcode_session_models::ToolExchangeResolution::Cancelled),
..active.clone()
};
let resolved = InteractionViewSummary {
state: bcode_session_view_models::InteractionViewState::Pending,
status_detail: None,
resolved: true,
resolution: Some(bcode_session_models::ToolExchangeResolution::Responded {
payload: serde_json::json!({"status": "future-resolved"}),
}),
..active.clone()
};
let active = format!(
"{:?}",
interaction_request(
&active,
Some(session_id),
&crate::context::tests::GuardedTestContext
)
);
let cancelled = format!(
"{:?}",
interaction_request(
&cancelled,
Some(session_id),
&crate::context::tests::GuardedTestContext
)
);
let resolved = format!(
"{:?}",
interaction_request(
&resolved,
Some(session_id),
&crate::context::tests::GuardedTestContext
)
);
assert!(active.contains("pending response"));
assert!(active.contains("controller snapshot"));
assert!(active.contains("Structured details truncated for display."));
assert!(active.contains("generic semantic controls"));
assert!(active.contains("generic-interaction-kind-label"));
assert!(active.contains("generic-interaction-control-label"));
assert!(active.contains("generic-interaction-value-label"));
assert!(active.contains("generic-interaction-direction-label"));
assert!(active.contains("/actions/interaction?"));
assert!(cancelled.contains("cancelled"));
assert!(!cancelled.contains("generic semantic controls"));
assert!(resolved.contains("future-resolved"));
assert!(resolved.contains("resolution"));
assert!(!resolved.contains("generic semantic controls"));
assert!(!resolved.contains("/actions/interaction?"));
}
#[test]
fn opaque_snapshot_does_not_override_semantic_interaction_state() {
let interaction = InteractionViewSummary {
producer_id: Some("example.plugin".to_owned()),
exchange_schema: Some("example.opaque".to_owned()),
exchange_schema_version: Some(1),
interaction_id: "opaque-1".to_owned(),
kind: "example.opaque".to_owned(),
tool_call_id: None,
title: None,
required: true,
snapshot: Some(serde_json::json!({"validation_error": "opaque data"})),
state: bcode_session_view_models::InteractionViewState::Pending,
status_detail: None,
resolved: false,
resolution: None,
};
let rendered = format!(
"{:?}",
interaction_request(
&interaction,
None,
&crate::context::tests::GuardedTestContext
)
);
assert!(rendered.contains("pending response"));
assert!(!rendered.contains("Validation error"));
}
#[test]
fn terminal_and_submitting_interactions_hide_controls_without_resolved_flag() {
for state in [
bcode_session_view_models::InteractionViewState::Resolved,
bcode_session_view_models::InteractionViewState::Cancelled,
bcode_session_view_models::InteractionViewState::Submitting,
] {
let interaction = InteractionViewSummary {
producer_id: None,
exchange_schema: None,
exchange_schema_version: None,
interaction_id: "terminal-1".to_owned(),
kind: "example.opaque".to_owned(),
tool_call_id: None,
title: None,
required: true,
snapshot: Some(serde_json::json!({"pending": true})),
state,
status_detail: None,
resolved: false,
resolution: None,
};
let rendered = format!(
"{:?}",
interaction_request(
&interaction,
Some(bcode_session_models::SessionId::new()),
&crate::context::tests::GuardedTestContext
)
);
assert!(!rendered.contains("send interaction input"));
assert!(!rendered.contains("generic semantic controls"));
assert_eq!(
rendered.contains("controller snapshot"),
state == bcode_session_view_models::InteractionViewState::Submitting
);
}
}
#[test]
fn resolved_interaction_does_not_display_stale_nonterminal_status() {
for state in [
bcode_session_view_models::InteractionViewState::Pending,
bcode_session_view_models::InteractionViewState::Submitting,
bcode_session_view_models::InteractionViewState::ValidationError,
bcode_session_view_models::InteractionViewState::ActionError,
] {
let mut interaction = InteractionViewSummary {
producer_id: None,
exchange_schema: None,
exchange_schema_version: None,
interaction_id: "resolved-1".to_owned(),
kind: "example.opaque".to_owned(),
tool_call_id: None,
title: None,
required: true,
snapshot: None,
state,
status_detail: Some("stale action failure".to_owned()),
resolved: true,
resolution: Some(bcode_session_models::ToolExchangeResolution::Cancelled),
};
let rendered = format!(
"{:?}",
interaction_request(
&interaction,
None,
&crate::context::tests::GuardedTestContext
)
);
assert!(rendered.contains("cancelled"));
assert!(!rendered.contains("stale action failure"));
for stale in [
"pending response",
"submitting",
"validation error",
"action error",
] {
assert!(!rendered.contains(stale));
}
interaction.resolution = Some(bcode_session_models::ToolExchangeResolution::Responded {
payload: serde_json::json!({"answer": true}),
});
let rendered = format!(
"{:?}",
interaction_request(
&interaction,
None,
&crate::context::tests::GuardedTestContext
)
);
assert!(rendered.contains("resolved"));
assert!(!rendered.contains("cancelled"));
assert!(!rendered.contains("stale action failure"));
interaction.state = bcode_session_view_models::InteractionViewState::Resolved;
interaction.status_detail = Some("response accepted".to_owned());
let rendered = format!(
"{:?}",
interaction_request(
&interaction,
None,
&crate::context::tests::GuardedTestContext
)
);
assert!(rendered.contains("response accepted"));
}
}
fn assert_question_labels_are_scoped(interaction: &InteractionViewSummary) {
let other = InteractionViewSummary {
interaction_id: "other-question".to_owned(),
..interaction.clone()
};
let rendered = format!(
"{:?}",
interaction_request(
&other,
Some(bcode_session_models::SessionId::new()),
&crate::context::tests::GuardedTestContext
)
);
for prefix in [
"question-0-option-0-label",
"question-0-custom-label",
"generic-interaction-kind",
"generic-interaction-kind-label",
"generic-interaction-control",
"generic-interaction-control-label",
"generic-interaction-value",
"generic-interaction-value-label",
"generic-interaction-format",
"generic-interaction-format-label",
"generic-interaction-direction",
"generic-interaction-direction-label",
] {
assert!(rendered.contains(&semantic_dom_id(prefix, &other.interaction_id)));
assert!(!rendered.contains(&semantic_dom_id(prefix, &interaction.interaction_id)));
}
}
#[test]
fn question_snapshot_renders_polished_controls_and_generic_fallback() {
let interaction = InteractionViewSummary {
producer_id: None,
exchange_schema: None,
exchange_schema_version: None,
interaction_id: "interaction-1".to_owned(),
kind: "bcode.question".to_owned(),
tool_call_id: Some("call-1".to_owned()),
title: Some("Choose".to_owned()),
required: true,
snapshot: Some(serde_json::json!({
"request": {
"questions": [{
"header": "Decision",
"question": "Proceed?",
"options": [{"label": "Yes", "value": "yes", "description": "Continue"}],
"control": "radio",
"selection_mode": "single",
"custom": true,
"custom_mode": "additional",
"required": true
}]
},
"validation_error": "Please answer required question 1 before submitting.",
"answers": [{"question_index": 0, "selected": ["yes"], "custom": null}],
"selected_option_indices": [[0]],
"focus": {"type": "question", "question_index": 0},
"focused_control_id": "question-0"
})),
state: bcode_session_view_models::InteractionViewState::ValidationError,
status_detail: Some("Please answer required question 1 before submitting.".to_owned()),
resolved: false,
resolution: None,
};
let rendered = format!(
"{:?}",
interaction_request(
&interaction,
Some(bcode_session_models::SessionId::new()),
&crate::context::tests::GuardedTestContext
)
);
assert!(rendered.contains("validation error"));
assert!(rendered.contains("Validation error"));
assert!(rendered.contains("Please answer required question 1 before submitting."));
assert!(rendered.contains("Proceed?"));
assert!(rendered.contains("Decision"));
assert!(rendered.contains("Continue"));
assert!(rendered.contains("Choose one option"));
assert!(rendered.contains("you may also provide a custom answer"));
assert!(rendered.contains("◉"));
assert!(rendered.contains(" *"));
assert!(rendered.contains("question-0.option-0"));
assert!(rendered.contains("question-0-option-0-label"));
assert!(rendered.contains("question-0-custom-label"));
assert!(rendered.contains("submit answers"));
assert!(rendered.contains("generic semantic controls"));
for label in [
"activate control",
"change control value",
"focus control",
"blur control",
"navigate focus",
"submit interaction",
"cancel interaction",
] {
assert!(rendered.contains(label));
}
assert!(rendered.contains("/actions/interaction?"));
for prefix in ["question-0-option-0-label", "question-0-custom-label"] {
assert!(rendered.contains(&semantic_dom_id(prefix, &interaction.interaction_id)));
}
assert_question_labels_are_scoped(&interaction);
let submitting = InteractionViewSummary {
state: bcode_session_view_models::InteractionViewState::Submitting,
status_detail: None,
..interaction
};
let rendered = format!(
"{:?}",
interaction_request(
&submitting,
Some(bcode_session_models::SessionId::new()),
&crate::context::tests::GuardedTestContext
)
);
assert!(rendered.contains("Proceed?"));
assert!(rendered.contains("submitting"));
assert!(rendered.contains("Yes"));
assert!(rendered.contains("Continue"));
assert!(rendered.contains("✓ "));
assert!(!rendered.contains("Please answer required question 1 before submitting."));
assert!(!rendered.contains("Validation error"));
assert!(!rendered.contains("/actions/interaction?"));
assert!(!rendered.contains("submit answers"));
assert!(!rendered.contains("generic semantic controls"));
}
#[test]
fn question_snapshot_renders_multiple_checkbox_and_exclusive_custom_semantics() {
let interaction = InteractionViewSummary {
producer_id: None,
exchange_schema: None,
exchange_schema_version: None,
interaction_id: "interaction-multiple".to_owned(),
kind: "bcode.question".to_owned(),
tool_call_id: Some("call-multiple".to_owned()),
title: Some("Choose several".to_owned()),
required: false,
snapshot: Some(serde_json::json!({
"request": {
"questions": [{
"header": null,
"question": "Which options?",
"options": [
{"label": "Alpha", "value": "a", "description": null},
{"label": "Beta", "value": "b", "description": "Second option"}
],
"control": "checkbox",
"selection_mode": "multiple",
"custom": true,
"custom_mode": "exclusive",
"required": false
}]
},
"answers": [{"question_index": 0, "selected": ["a"], "custom": "other"}],
"selected_option_indices": [[0]],
"focus": {"type": "custom", "question_index": 0},
"focused_control_id": "question-0.custom"
})),
state: bcode_session_view_models::InteractionViewState::Pending,
status_detail: None,
resolved: false,
resolution: None,
};
let containers = interaction_request(
&interaction,
Some(bcode_session_models::SessionId::new()),
&crate::context::StaticPresentationContext,
);
let mut text = String::new();
for container in &containers {
container_text(container, &mut text);
}
let rendered = format!("{containers:?}");
assert!(text.contains("Choose one or more options"));
assert!(text.contains("or provide a custom answer"));
assert!(text.contains("☑ Alpha"));
assert!(text.contains("☐ Beta"));
assert!(text.contains("Second option"));
assert!(rendered.contains("other"));
let read_only = interaction_request(
&interaction,
None,
&crate::context::StaticPresentationContext,
);
let mut text = String::new();
for container in &read_only {
container_text(container, &mut text);
}
assert!(text.contains("✓ Alpha"));
assert!(text.contains("○ Beta"));
assert!(text.contains("Second option"));
assert!(text.contains("Custom answer: other"));
assert!(!text.contains("submit answers"));
assert!(!format!("{read_only:?}").contains("/actions/interaction?"));
}
#[test]
fn filesystem_query_adapters_cover_empty_structured_metadata_and_bounded_snippets() {
let result = |schema: &str, title: &str, metadata| {
render_tool_result(&ToolResultView::Artifact {
artifact: ToolArtifactView::from(ToolArtifact {
artifact_id: format!("query-{schema}"),
producer_plugin_id: "bcode.filesystem".to_owned(),
schema: schema.to_owned(),
schema_version: 1,
tool_call_id: Some("call-query".to_owned()),
title: Some(title.to_owned()),
metadata,
refs: Vec::new(),
}),
})
};
let exists = format!(
"{:?}",
result(
"bcode.filesystem.exists",
"Path exists",
serde_json::json!({"path": "/tmp/missing", "exists": false}),
)
);
let list = format!(
"{:?}",
result(
"bcode.filesystem.list",
"Directory entries",
serde_json::json!({
"entries": [], "backend": "rust", "partial": false,
"timed_out": false, "visited_entries": 0
}),
)
);
let find = format!(
"{:?}",
result(
"bcode.filesystem.find",
"Path matches",
serde_json::json!({
"paths": [], "backend": "rust", "partial": true,
"timed_out": false, "visited_entries": 42,
"message": "result limit reached"
}),
)
);
let grep = format!(
"{:?}",
result(
"bcode.filesystem.grep",
"Text matches",
serde_json::json!({
"matches": [{
"path": "/tmp/example.rs",
"line_number": 77,
"line": "g".repeat(2_100)
}],
"backend": "ripgrep", "partial": true,
"timed_out": true, "visited_entries": 100
}),
)
);
let stat = format!(
"{:?}",
result(
"bcode.filesystem.stat",
"Path metadata",
serde_json::json!({
"path": "/tmp/example.rs", "exists": true,
"kind": "file", "len": 1234
}),
)
);
assert!(exists.contains("Path does not exist"));
assert!(exists.contains("/tmp/missing"));
assert!(list.contains("No directory entries."));
assert!(list.contains("visited entries"));
assert!(find.contains("No matching paths."));
assert!(find.contains("result limit reached"));
assert!(grep.contains("/tmp/example.rs"));
assert!(grep.contains("77"));
assert!(grep.contains('…'));
assert!(grep.contains("timed out"));
assert!(stat.contains("Path exists"));
assert!(stat.contains("file"));
assert!(stat.contains("1234"));
}
#[test]
fn filesystem_change_adapter_renders_bounded_split_diff_and_line_context() {
let result = ToolResultView::Artifact {
artifact: ToolArtifactView::from(ToolArtifact {
artifact_id: "filesystem-change-edge".to_owned(),
producer_plugin_id: "bcode.filesystem".to_owned(),
schema: "bcode.filesystem.change".to_owned(),
schema_version: 1,
tool_call_id: Some("call-change".to_owned()),
title: Some("File change".to_owned()),
metadata: serde_json::json!({
"tool_name": "filesystem.edit",
"summary": "updated configuration",
"path": "/tmp/config.rs",
"old_text": format!("old_one{}\nold_two", "o".repeat(17_000)),
"new_text": format!("new_one{}\nnew_two", "n".repeat(17_000)),
"old_start_line": 10,
"new_start_line": 12
}),
refs: Vec::new(),
}),
};
let containers = render_tool_result(&result);
let mut text = String::new();
for container in &containers {
container_text(container, &mut text);
}
assert!(text.contains("/tmp/config.rs"));
assert!(text.contains("updated configuration"));
assert!(text.contains("operation: filesystem.edit"));
assert!(text.contains("removed · lines 10–11"));
assert!(text.contains("added · lines 12–13"));
assert!(text.contains("old_one"));
assert!(text.contains("new_one"));
assert!(text.contains("Removed text truncated for display."));
assert!(text.contains("Added text truncated for display."));
}
#[test]
fn filesystem_read_adapter_renders_path_range_language_truncation_and_continuation() {
let result = ToolResultView::Artifact {
artifact: ToolArtifactView::from(ToolArtifact {
artifact_id: "filesystem-read-edge".to_owned(),
producer_plugin_id: "bcode.filesystem".to_owned(),
schema: "bcode.filesystem.read".to_owned(),
schema_version: 1,
tool_call_id: Some("call-read".to_owned()),
title: Some("File contents".to_owned()),
metadata: serde_json::json!({
"path": "/tmp/example.rs",
"start_line": 11,
"end_line": 20,
"total_lines": 100,
"returned_bytes": 33_000,
"total_bytes": 50_000,
"truncated": true,
"contents": format!("fn main() {{}}\n{}", "x".repeat(33_000))
}),
refs: vec![bcode_session_models::ToolArtifactRef {
key: "full-file".to_owned(),
content_type: Some("text/x-rust".to_owned()),
storage_uri: Some("file:///tmp/example.rs".to_owned()),
byte_len: Some(50_000),
metadata: None,
}],
}),
};
let containers = render_tool_result(&result);
let mut text = String::new();
for container in &containers {
container_text(container, &mut text);
}
let rendered = format!("{containers:?}");
assert!(text.contains("/tmp/example.rs"));
assert!(text.contains("lines 11–20 of 100"));
assert!(text.contains("33000 of 50000 bytes"));
assert!(text.contains("rust"));
assert!(text.contains("fn main() {}"));
assert!(text.contains("File contents truncated for display."));
assert!(text.contains("More file content is available."));
assert!(text.contains("Continue at offset 21."));
assert!(text.contains("artifact references"));
assert!(text.contains("full-file"));
assert!(rendered.contains("markdown"));
}
#[test]
fn document_and_ocr_results_bound_text_and_show_metadata_and_references() {
let reference = bcode_session_models::ToolArtifactRef {
key: "text-sidecar".to_owned(),
content_type: Some("text/plain".to_owned()),
storage_uri: Some("file:///tmp/extracted.txt".to_owned()),
byte_len: Some(40_000),
metadata: None,
};
let document = ToolResultView::Artifact {
artifact: ToolArtifactView::from(ToolArtifact {
artifact_id: "document-edge".to_owned(),
producer_plugin_id: "bcode.document".to_owned(),
schema: "bcode.document.extract_result".to_owned(),
schema_version: 1,
tool_call_id: Some("call-document".to_owned()),
title: Some("Document extraction".to_owned()),
metadata: serde_json::json!({
"source": "file:///tmp/source.pdf",
"content_type": "application/pdf",
"extractor": "pdftotext",
"truncated": true,
"text": "d".repeat(33_000)
}),
refs: vec![reference.clone()],
}),
};
let ocr = ToolResultView::Artifact {
artifact: ToolArtifactView::from(ToolArtifact {
artifact_id: "ocr-edge".to_owned(),
producer_plugin_id: "bcode.ocr".to_owned(),
schema: "bcode.ocr.extract_result".to_owned(),
schema_version: 1,
tool_call_id: Some("call-ocr".to_owned()),
title: Some("OCR extraction".to_owned()),
metadata: serde_json::json!({
"source": {"path": "/tmp/image.png"},
"engine": "tesseract",
"language": "eng",
"text_bytes": 33_000,
"truncated": true,
"text": "o".repeat(33_000)
}),
refs: vec![reference],
}),
};
let document = format!("{:?}", render_tool_result(&document));
let ocr = format!("{:?}", render_tool_result(&ocr));
for rendered in [&document, &ocr] {
assert!(rendered.contains("Source extraction was truncated."));
assert!(rendered.contains("Extracted text truncated for display."));
assert!(rendered.contains("artifact references"));
assert!(rendered.contains("text-sidecar"));
assert!(rendered.contains("file:///tmp/extracted.txt"));
}
assert!(document.contains("application/pdf"));
assert!(document.contains("pdftotext"));
assert!(ocr.contains("tesseract"));
assert!(ocr.contains("eng"));
}
#[test]
fn bundled_visual_registry_covers_actual_high_value_request_schemas() {
for schema in [
"bcode.filesystem.request",
"bcode.filesystem.request-draft.write",
"bcode.filesystem.request-draft.edit",
"bcode.filesystem.change",
"bcode.filesystem.read",
"bcode.filesystem.image",
"bcode.filesystem.exists",
"bcode.filesystem.list",
"bcode.filesystem.find",
"bcode.filesystem.grep",
"bcode.filesystem.stat",
"bcode.filesystem.artifact.metadata",
"bcode.filesystem.artifact.read",
"bcode.filesystem.artifact.grep",
"bcode.document.request",
"bcode.ocr.request",
"bcode.web-search.search_request",
"bcode.web-search.fetch_request",
"bcode.web-search.status_request",
"bcode.web-search.inspect_request",
"bcode.git.clone_request",
"bcode.worktree.request",
"bcode.vim-edit.request.preview",
"bcode.vim-edit.request.apply",
"bcode.vim-edit.live",
"bcode.vim-edit.playback",
] {
assert!(
VISUAL_ADAPTERS.contains_key(&(schema, 1)),
"missing {schema}"
);
}
}
#[test]
fn hyperchad_request_draft_adapters_render_incomplete_plugin_owned_visuals() {
for (schema, expected_title) in [
(
"bcode.filesystem.request-draft.write",
"Filesystem write · assembling…",
),
(
"bcode.filesystem.request-draft.edit",
"Filesystem edit · assembling…",
),
(
"bcode.vim-edit.request-draft.preview",
"Vim edit · assembling…",
),
(
"bcode.vim-edit.request-draft.apply",
"Vim edit · assembling…",
),
] {
let contribution = bcode_session_models::ToolContributionEvent {
invocation_id: "call-draft".to_owned(),
contribution_id: "draft".to_owned(),
sequence: 1,
producer_id: schema
.split(".request-draft")
.next()
.unwrap_or("fixture")
.to_owned(),
schema: schema.to_owned(),
schema_version: 1,
operation: bcode_session_models::ToolContributionOperation::Upsert,
persistence: bcode_session_models::ToolContributionPersistence::Transient,
artifact: None,
payload: serde_json::json!({
"preview": "{\"path\":\"src/lib.rs\",\"steps\":[",
"argument_bytes": 37,
"truncated": false
}),
};
let rendered = render_tool_contribution(
&contribution,
bcode_session_models::ToolContributionPlacement::Request,
);
let text = container_text_all(&rendered);
assert!(text.contains(expected_title), "{schema}: {text}");
assert!(text.contains("incomplete arguments"), "{schema}: {text}");
assert!(text.contains("37 argument bytes"), "{schema}: {text}");
assert!(!text.contains("preview"), "{schema}: {text}");
}
}
#[test]
fn unknown_contribution_has_no_raw_hyperchad_fallback() {
let kind = TranscriptViewItemKind::ToolContribution {
invocation: None,
placement: bcode_session_models::ToolContributionPlacement::Request,
contribution: bcode_session_models::ToolContributionEvent {
invocation_id: "call".to_owned(),
contribution_id: "surface".to_owned(),
sequence: 9,
producer_id: "future.producer".to_owned(),
schema: "future.unknown/schema".to_owned(),
schema_version: 77,
operation: bcode_session_models::ToolContributionOperation::Append,
persistence: bcode_session_models::ToolContributionPersistence::Durable,
artifact: None,
payload: serde_json::json!({"sentinel": "opaque-web"}),
},
};
let rendered = format!("{:?}", transcript_item_body(&kind));
assert!(!rendered.contains("future.unknown/schema"));
assert!(!rendered.contains("opaque-web"));
assert!(!rendered.contains("append"));
}
#[test]
fn git_contribution_renders_through_schema_adapter_without_fallback() {
let kind = TranscriptViewItemKind::ToolContribution {
invocation: None,
placement: bcode_session_models::ToolContributionPlacement::Request,
contribution: bcode_session_models::ToolContributionEvent {
invocation_id: "git-call".to_owned(),
contribution_id: "clone-request".to_owned(),
sequence: 1,
producer_id: "bcode.git".to_owned(),
schema: "bcode.git.clone_request".to_owned(),
schema_version: 1,
operation: bcode_session_models::ToolContributionOperation::Upsert,
persistence: bcode_session_models::ToolContributionPersistence::Durable,
artifact: None,
payload: serde_json::json!({
"url": "https://github.com/bmorphism/bcode",
"ref": "main"
}),
},
};
let rendered = format!("{:?}", transcript_item_body(&kind));
assert!(rendered.contains("github.com/bmorphism/bcode"));
assert!(rendered.contains("main"));
assert!(!rendered.contains("bcode.git.clone_request"));
}
#[test]
fn unsupported_shell_contribution_has_no_raw_hyperchad_fallback() {
let kind = TranscriptViewItemKind::ToolContribution {
invocation: None,
placement: bcode_session_models::ToolContributionPlacement::Request,
contribution: bcode_session_models::ToolContributionEvent {
invocation_id: "shell-call".to_owned(),
contribution_id: "shell-run-summary".to_owned(),
sequence: 1,
producer_id: "bcode.shell".to_owned(),
schema: "bcode.shell.run.summary".to_owned(),
schema_version: 1,
operation: bcode_session_models::ToolContributionOperation::Upsert,
persistence: bcode_session_models::ToolContributionPersistence::Durable,
artifact: None,
payload: serde_json::json!({"output": "shell-render-sentinel"}),
},
};
let rendered = format!("{:?}", transcript_item_body(&kind));
assert!(!rendered.contains("bcode.shell.run.summary"));
assert!(!rendered.contains("shell-render-sentinel"));
}
#[test]
fn local_artifact_paths_and_storage_uris_never_become_unguarded_resources() {
let artifact = ToolArtifactView::from(ToolArtifact {
artifact_id: "local-resource-fixture".to_owned(),
producer_plugin_id: "bcode.filesystem".to_owned(),
schema: "bcode.filesystem.image".to_owned(),
schema_version: 1,
tool_call_id: Some("local-resource-call".to_owned()),
title: Some("Local image".to_owned()),
metadata: serde_json::json!({
"path": "/tmp/private-image.png",
"mime_type": "image/png",
"width": 640,
"height": 480,
"byte_len": 1024
}),
refs: vec![bcode_session_models::ToolArtifactRef {
key: "private-image".to_owned(),
content_type: Some("image/png".to_owned()),
storage_uri: Some("file:///tmp/private-image.png".to_owned()),
byte_len: Some(1024),
metadata: None,
}],
});
let containers = render_tool_result(&ToolResultView::Artifact { artifact });
let text = container_text_all(&containers);
let mut urls = Vec::new();
for container in &containers {
collect_resource_urls(container, &mut urls);
}
assert!(text.contains("/tmp/private-image.png"));
assert!(!text.contains("file:///tmp/private-image.png"));
assert!(text.contains("Image preview is protected."));
assert!(urls.is_empty());
}
#[test]
fn filesystem_image_adapter_never_uses_untrusted_storage_uris_directly() {
let artifact = |storage_uri: Option<&str>, content_type: Option<&str>| {
ToolArtifactView::from(ToolArtifact {
artifact_id: "image-preview-fixture".to_owned(),
producer_plugin_id: "bcode.filesystem".to_owned(),
schema: "bcode.filesystem.image".to_owned(),
schema_version: 1,
tool_call_id: Some("image-preview-call".to_owned()),
title: Some("Preview image".to_owned()),
metadata: serde_json::json!({
"path": "/workspace/preview.png",
"mime_type": "image/png",
"width": 320,
"height": 200,
"byte_len": 256
}),
refs: vec![bcode_session_models::ToolArtifactRef {
key: "inline-image".to_owned(),
content_type: content_type.map(str::to_owned),
storage_uri: storage_uri.map(str::to_owned),
byte_len: Some(256),
metadata: None,
}],
})
};
let protected = render_tool_result(&ToolResultView::Artifact {
artifact: artifact(Some("/untrusted/plugin/path"), Some("image/png")),
});
let protected_text = container_text_all(&protected);
let mut protected_urls = Vec::new();
for container in &protected {
collect_resource_urls(container, &mut protected_urls);
}
assert!(protected_urls.is_empty());
assert!(protected_text.contains("Image preview is protected."));
assert!(protected_text.contains("guarded artifact: inline-image"));
assert!(protected_text.contains("dimensions: 320x200"));
assert!(protected_text.contains("type: image/png"));
for (uri, content_type) in [
("javascript:alert(1)", "image/png"),
("https://external.example/private.png", "image/png"),
("/guarded/vector.svg", "image/svg+xml"),
("/guarded/not-an-image", "text/plain"),
("file:///tmp/private.png", "image/png"),
] {
let unsafe_result = render_tool_result(&ToolResultView::Artifact {
artifact: artifact(Some(uri), Some(content_type)),
});
let mut urls = Vec::new();
for container in &unsafe_result {
collect_resource_urls(container, &mut urls);
}
assert!(urls.is_empty(), "unsafe resource escaped: {uri}");
}
}
#[test]
fn filesystem_image_adapter_uses_guarded_context_resource() {
let session_id = bcode_session_models::SessionId::new();
let artifact = ToolArtifactView::from(ToolArtifact {
artifact_id: "image artifact / one".to_owned(),
producer_plugin_id: "bcode.filesystem".to_owned(),
schema: "bcode.filesystem.image".to_owned(),
schema_version: 1,
tool_call_id: Some("image-preview-call".to_owned()),
title: Some("Preview image".to_owned()),
metadata: serde_json::json!({
"path": "/workspace/preview.png",
"mime_type": "image/png",
"width": 320,
"height": 200,
"byte_len": 256
}),
refs: vec![bcode_session_models::ToolArtifactRef {
key: "inline image".to_owned(),
content_type: Some("image/png".to_owned()),
storage_uri: Some("bcode-artifact://invocation/private/private".to_owned()),
byte_len: Some(256),
metadata: None,
}],
});
let containers = render_tool_result_with_context(
&ToolResultView::Artifact { artifact },
Some(session_id),
&crate::context::tests::GuardedTestContext,
);
let mut urls = Vec::new();
for container in &containers {
collect_resource_urls(container, &mut urls);
}
assert_eq!(urls.len(), 1);
assert!(urls[0].starts_with(&format!("/artifacts/{session_id}?")));
assert!(urls[0].contains("test-capability=opaque"));
assert!(urls[0].contains("artifact_id=image artifact / one"));
assert!(urls[0].contains("reference_key=inline image"));
assert!(!format!("{containers:?}").contains("bcode-artifact://"));
}
#[test]
fn registered_adapters_reject_missing_required_fields_and_unsupported_versions() {
for (schema, schema_version) in VISUAL_ADAPTERS.keys() {
let missing = bcode_session_models::ToolContributionEvent {
invocation_id: "missing-call".to_owned(),
contribution_id: "missing-contribution".to_owned(),
sequence: 1,
producer_id: "fixture-plugin".to_owned(),
schema: (*schema).to_owned(),
schema_version: *schema_version,
operation: bcode_session_models::ToolContributionOperation::Upsert,
persistence: bcode_session_models::ToolContributionPersistence::Durable,
artifact: None,
payload: serde_json::json!({}),
};
let rendered = super::adapters::render_tool_contribution(
&missing,
bcode_session_models::ToolContributionPlacement::Request,
);
let rendered = container_text_all(&rendered);
assert!(!rendered.is_empty());
assert!(rendered.len() < 32_000);
let unsupported = bcode_session_models::ToolContributionEvent {
schema_version: schema_version.saturating_add(1),
..missing
};
let rendered = super::adapters::render_tool_contribution(
&unsupported,
bcode_session_models::ToolContributionPlacement::Request,
);
assert!(container_text_all(&rendered).contains("tool request"));
}
for (schema, schema_version) in ARTIFACT_ADAPTERS.keys() {
let missing = ToolArtifactView::from(ToolArtifact {
artifact_id: "missing-artifact".to_owned(),
producer_plugin_id: "fixture-plugin".to_owned(),
schema: (*schema).to_owned(),
schema_version: *schema_version,
tool_call_id: None,
title: None,
metadata: serde_json::json!({}),
refs: Vec::new(),
});
let rendered_missing = format!(
"{:?}",
render_tool_result(&ToolResultView::Artifact {
artifact: missing.clone()
})
);
assert!(!rendered_missing.is_empty());
assert!(rendered_missing.len() < 64_000);
let unsupported = ToolArtifactView::from(ToolArtifact {
schema_version: schema_version.saturating_add(1),
..missing.artifact
});
let rendered = format!(
"{:?}",
render_tool_result(&ToolResultView::Artifact {
artifact: unsupported
})
);
assert!(rendered.contains("artifact details"));
}
}
#[test]
fn rich_adapter_state_matrix_covers_empty_truncated_optional_error_and_fallbacks() {
let empty = ToolArtifactView::from(ToolArtifact {
artifact_id: "empty-search".to_owned(),
producer_plugin_id: "bcode.web-search".to_owned(),
schema: "bcode.web-search.search_results".to_owned(),
schema_version: 1,
tool_call_id: None,
title: None,
metadata: serde_json::json!({"query": "none", "results": []}),
refs: Vec::new(),
});
let empty = format!(
"{:?}",
render_tool_result(&ToolResultView::Artifact { artifact: empty })
);
assert!(empty.contains("No search results"));
let optional = ToolArtifactView::from(ToolArtifact {
artifact_id: "minimal-worktree".to_owned(),
producer_plugin_id: "bcode.worktree".to_owned(),
schema: "bcode.worktree.create_result".to_owned(),
schema_version: 1,
tool_call_id: None,
title: None,
metadata: serde_json::json!({"path": "/tmp/minimal"}),
refs: Vec::new(),
});
assert!(
format!(
"{:?}",
render_tool_result(&ToolResultView::Artifact { artifact: optional })
)
.contains("/tmp/minimal")
);
let truncated = ToolArtifactView::from(ToolArtifact {
artifact_id: "truncated-read".to_owned(),
producer_plugin_id: "bcode.filesystem".to_owned(),
schema: "bcode.filesystem.read".to_owned(),
schema_version: 1,
tool_call_id: None,
title: None,
metadata: serde_json::json!({
"path": "/tmp/large.txt",
"contents": "x".repeat(40_000),
"truncated": true
}),
refs: Vec::new(),
});
let truncated = format!(
"{:?}",
render_tool_result(&ToolResultView::Artifact {
artifact: truncated
})
);
assert!(truncated.contains("truncated"));
let fallback = ToolArtifactView::from(ToolArtifact {
artifact_id: "unknown".to_owned(),
producer_plugin_id: "unknown.plugin".to_owned(),
schema: "unknown.schema".to_owned(),
schema_version: 1,
tool_call_id: None,
title: None,
metadata: serde_json::json!({"summary": "fallback details"}),
refs: Vec::new(),
});
let fallback = format!(
"{:?}",
render_tool_result(&ToolResultView::Artifact { artifact: fallback })
);
assert!(fallback.contains("artifact details"));
for status in [
ToolInvocationViewStatus::Requested,
ToolInvocationViewStatus::Running,
ToolInvocationViewStatus::Waiting,
ToolInvocationViewStatus::Finished,
ToolInvocationViewStatus::Failed,
ToolInvocationViewStatus::Cancelled,
] {
let rendered = container_text_all(&transcript_item(&transcript_fixture_item(
&format!("state-matrix-{status:?}"),
matches!(
status,
ToolInvocationViewStatus::Running | ToolInvocationViewStatus::Waiting
),
TranscriptViewItemKind::ToolInvocation {
tool: Box::new(tool_fixture(status)),
},
)));
assert!(rendered.contains(match status {
ToolInvocationViewStatus::Requested => "requested",
ToolInvocationViewStatus::Running => "running",
ToolInvocationViewStatus::Waiting => "waiting",
ToolInvocationViewStatus::Finished => "finished",
ToolInvocationViewStatus::Failed => "failed",
ToolInvocationViewStatus::Cancelled => "cancelled",
}));
}
}
#[test]
fn shared_tool_presentation_fixtures_render_semantic_content_in_hyperchad() {
for fixture in crate::renderer_fixtures::renderer_tool_presentation_fixtures() {
let rendered = container_text_all(&transcript_item(&fixture.item));
for expected in &fixture.expected {
assert!(
rendered.contains(expected),
"{} missing {expected:?}: {rendered}",
fixture.name
);
}
for forbidden in &fixture.forbidden {
assert!(
!rendered.contains(forbidden),
"{} exposed {forbidden:?}: {rendered}",
fixture.name
);
}
for revision in &fixture.revisions {
let revised = container_text_all(&transcript_item(revision));
let TranscriptViewItemKind::ToolInvocation { tool } = &revision.kind else {
unreachable!("shared fixture revisions are tool invocations");
};
assert!(revised.contains(tool.tool_name.as_deref().unwrap_or("Tool")));
for forbidden in &fixture.forbidden {
assert!(
!revised.contains(forbidden),
"{} exposed {forbidden:?}",
fixture.name
);
}
}
let TranscriptViewItemKind::ToolInvocation { tool } = &fixture.item.kind else {
unreachable!("shared fixtures are tool invocations");
};
let status = if tool.timing.timed_out == Some(true) {
"timed out"
} else if tool.is_error == Some(true) {
"failed"
} else {
match tool.status {
ToolInvocationViewStatus::Requested => "requested",
ToolInvocationViewStatus::Running => "running",
ToolInvocationViewStatus::Waiting => "waiting",
ToolInvocationViewStatus::Finished => "finished",
ToolInvocationViewStatus::Failed => "failed",
ToolInvocationViewStatus::Cancelled => "cancelled",
}
};
assert!(
rendered.contains(status),
"{} missing status {status:?}: {rendered}",
fixture.name
);
}
}
#[test]
fn every_producer_family_renders_canonical_lifecycle_fallback_in_hyperchad() {
let producer_families = [
"shell",
"filesystem",
"vim-edit",
"document",
"ocr",
"web-search",
"git",
"worktree",
];
let fixtures = crate::renderer_fixtures::renderer_tool_presentation_fixtures();
for name in producer_families {
let fixture = fixtures
.iter()
.find(|fixture| fixture.name == name)
.unwrap_or_else(|| panic!("missing {name} producer fixture"));
for status in [
ToolInvocationViewStatus::Requested,
ToolInvocationViewStatus::Running,
ToolInvocationViewStatus::Waiting,
ToolInvocationViewStatus::Finished,
ToolInvocationViewStatus::Failed,
ToolInvocationViewStatus::Cancelled,
] {
let mut variant = fixture.item.clone();
variant.streaming = matches!(
status,
ToolInvocationViewStatus::Running | ToolInvocationViewStatus::Waiting
);
let TranscriptViewItemKind::ToolInvocation { tool } = &mut variant.kind else {
panic!("{name} fixture must be a tool invocation");
};
tool.status = status;
tool.presentation = None;
tool.result_text = matches!(
status,
ToolInvocationViewStatus::Finished | ToolInvocationViewStatus::Failed
)
.then(|| format!("{name} terminal result"));
tool.is_error = matches!(status, ToolInvocationViewStatus::Failed).then_some(true);
let tool_name = tool.tool_name.clone();
let result_text = tool.result_text.clone();
let rendered = container_text_all(&transcript_item(&variant));
assert!(rendered.contains(tool_name.as_deref().unwrap_or("Tool")));
assert!(rendered.contains(match status {
ToolInvocationViewStatus::Requested => "requested",
ToolInvocationViewStatus::Running => "running",
ToolInvocationViewStatus::Waiting => "waiting",
ToolInvocationViewStatus::Finished => "finished",
ToolInvocationViewStatus::Failed => "failed",
ToolInvocationViewStatus::Cancelled => "cancelled",
}));
if let Some(result) = &result_text {
assert!(rendered.contains(result));
}
}
}
}
#[test]
fn primary_tool_presentation_uses_schema_adapter_and_falls_back_without_raw_payload() {
let mut tool = tool_fixture(ToolInvocationViewStatus::Running);
tool.presentation = Some(ToolPresentationView {
producer_id: "bcode.shell".to_owned(),
generation: 0,
revision: 2,
retention: bcode_session_view_models::ToolPresentationRetention::RetainLatest,
schema: "bcode.tool.request.shell.run".to_owned(),
schema_version: 1,
artifact: None,
payload: serde_json::json!({"command": "cargo test", "cwd": "/tmp/project"}),
});
let item = transcript_fixture_item(
"primary-presentation",
true,
TranscriptViewItemKind::ToolInvocation {
tool: Box::new(tool.clone()),
},
);
let rendered = container_text_all(&transcript_item(&item));
assert!(rendered.contains("cargo test"));
assert!(!rendered.contains("developer arguments"));
tool.presentation = Some(ToolPresentationView {
producer_id: "example.plugin".to_owned(),
generation: 0,
revision: 3,
retention: bcode_session_view_models::ToolPresentationRetention::RetainLatest,
schema: "example.unsupported".to_owned(),
schema_version: 1,
artifact: None,
payload: serde_json::json!({"secret": "must-not-render"}),
});
let item = transcript_fixture_item(
"unsupported-primary-presentation",
true,
TranscriptViewItemKind::ToolInvocation {
tool: Box::new(tool),
},
);
let rendered = container_text_all(&transcript_item(&item));
assert!(rendered.contains("developer arguments"));
assert!(!rendered.contains("must-not-render"));
}
#[test]
fn repeated_malformed_presentations_keep_bounded_semantic_fallback() {
let secret = "opaque-malformed-presentation-secret";
for revision in 1..=256 {
let mut tool = tool_fixture(ToolInvocationViewStatus::Running);
tool.tool_call_id = "call-malformed".to_owned();
tool.tool_name = Some("shell.run".to_owned());
tool.arguments_json = Some(r#"{"command":"cargo test"}"#.to_owned());
tool.presentation = Some(ToolPresentationView {
producer_id: "bcode.shell".to_owned(),
generation: 0,
revision,
retention: bcode_session_view_models::ToolPresentationRetention::RetainLatest,
schema: "bcode.tool.request.shell.run".to_owned(),
schema_version: 1,
artifact: None,
payload: serde_json::json!({"command": {"secret": secret}}),
});
let item = transcript_fixture_item(
"malformed-primary-presentation",
true,
TranscriptViewItemKind::ToolInvocation {
tool: Box::new(tool),
},
);
let rendered = container_text_all(&transcript_item(&item));
assert!(rendered.contains("shell.run"));
assert!(rendered.contains("cargo test"));
assert!(!rendered.contains(secret));
assert!(rendered.len() < 2_048);
}
}
#[test]
fn every_registered_visual_adapter_has_a_fixture() {
for ((schema, schema_version), adapter) in VISUAL_ADAPTERS.iter() {
let contribution = bcode_session_models::ToolContributionEvent {
invocation_id: format!("fixture-call:{schema}"),
contribution_id: format!("fixture:{schema}:{schema_version}"),
sequence: 1,
producer_id: "fixture-plugin".to_owned(),
schema: (*schema).to_owned(),
schema_version: *schema_version,
operation: bcode_session_models::ToolContributionOperation::Upsert,
persistence: bcode_session_models::ToolContributionPersistence::Durable,
artifact: None,
payload: visual_adapter_fixture(schema),
};
let rich = adapter(&contribution).unwrap_or_else(|| {
panic!("visual adapter fixture did not render {schema}@{schema_version}")
});
let rendered = format!("{rich:?}");
assert!(!rendered.is_empty());
assert!(!rendered.contains("tool request"));
}
}
fn visual_adapter_fixture(schema: &str) -> serde_json::Value {
let arguments = match schema {
"bcode.tool.request.shell.run" => serde_json::json!({
"command": "cargo test",
"cwd": "/tmp/fixture"
}),
"bcode.filesystem.request-draft.write" => {
return serde_json::json!({
"preview": r#"{"path":"/tmp/fixture.rs","contents":"fixture"}"#,
"argument_bytes": 55,
"truncated": false
});
}
"bcode.filesystem.request-draft.edit" => {
return serde_json::json!({
"preview": r#"{"path":"/tmp/fixture.rs","old_text":"old","new_text":"new"}"#,
"argument_bytes": 72,
"truncated": false
});
}
"bcode.filesystem.change" => serde_json::json!({
"path": "/tmp/fixture.rs",
"old_text": "old",
"new_text": "new"
}),
schema if schema.starts_with("bcode.filesystem") => serde_json::json!({
"operation": schema.rsplit('.').next().unwrap_or("filesystem"),
"path": "/tmp/fixture.rs"
}),
"bcode.document.request" | "bcode.ocr.request" => serde_json::json!({
"operation": "extract",
"path": "/tmp/fixture.pdf"
}),
"bcode.web-search.search_request" => serde_json::json!({
"query": "fixture query",
"max_results": 5
}),
"bcode.web-search.fetch_request" => serde_json::json!({
"url": "https://example.com/fixture"
}),
"bcode.web-search.status_request" | "bcode.web-search.inspect_request" => {
serde_json::json!({
"operation": schema.rsplit('.').next().unwrap_or("web"),
"url": "https://example.com/fixture"
})
}
"bcode.git.clone_request" => serde_json::json!({
"url": "https://example.com/repository.git",
"destination": "/tmp/repository",
"ref": "main"
}),
"bcode.worktree.request" => serde_json::json!({
"operation": "create",
"name": "fixture",
"path": "/tmp/worktree",
"branch": "fixture"
}),
"bcode.vim-edit.request.preview" | "bcode.vim-edit.request.apply" => {
serde_json::json!({
"operation": "preview",
"path": "/tmp/fixture.rs",
"steps": [{"insert": "fixture"}]
})
}
"bcode.vim-edit.request-draft.preview" | "bcode.vim-edit.request-draft.apply" => {
return serde_json::json!({
"preview": "{\"path\":\"/tmp/fixture.rs\",\"steps\":[",
"argument_bytes": 42,
"truncated": false
});
}
"bcode.vim-edit.live" => serde_json::json!({
"phase": "running",
"path": "/tmp/fixture.rs",
"step_index": 1,
"step_total": 2,
"message": "applying edit"
}),
"bcode.vim-edit.playback" => serde_json::json!({
"success": true,
"path": "/tmp/fixture.rs",
"tool_mode": "preview",
"changed": true,
"summary": "edit completed",
"diff": "-old\n+new"
}),
_ => serde_json::json!({"operation": "fixture"}),
};
if matches!(schema, "bcode.vim-edit.live" | "bcode.vim-edit.playback") {
arguments
} else {
serde_json::json!({"arguments": arguments})
}
}
#[test]
fn every_registered_artifact_adapter_has_a_fixture() {
for ((schema, schema_version), adapter) in ARTIFACT_ADAPTERS.iter() {
let artifact = ToolArtifactView::from(ToolArtifact {
artifact_id: format!("fixture:{schema}:{schema_version}"),
producer_plugin_id: "fixture-plugin".to_owned(),
schema: (*schema).to_owned(),
schema_version: *schema_version,
tool_call_id: Some("fixture-call".to_owned()),
title: Some(format!("Fixture {schema}")),
metadata: artifact_adapter_fixture_metadata(schema),
refs: Vec::new(),
});
let _rich = adapter(&artifact).unwrap_or_else(|| {
panic!("artifact adapter fixture did not render {schema}@{schema_version}")
});
let rendered = format!(
"{:?}",
render_tool_result(&ToolResultView::Artifact { artifact })
);
assert!(!rendered.contains("artifact details"));
}
}
fn artifact_adapter_fixture_metadata(schema: &str) -> serde_json::Value {
document_artifact_fixture(schema)
.or_else(|| filesystem_artifact_fixture(schema))
.or_else(|| ocr_artifact_fixture(schema))
.or_else(|| web_and_worktree_artifact_fixture(schema))
.unwrap_or_else(|| serde_json::json!({"fixture": true}))
}
fn document_artifact_fixture(schema: &str) -> Option<serde_json::Value> {
match schema {
"bcode.document.extract_result" => Some(serde_json::json!({
"source": "file:///tmp/fixture.pdf",
"content_type": "application/pdf",
"artifact_kind": "document",
"text": "fixture document text",
"truncated": false,
"extractor": "native"
})),
"bcode.document.status" => Some(serde_json::json!({
"extract": {
"available": true,
"extractors": [{
"name": "fixture-extractor",
"available": true,
"quality": "fixture-quality"
}],
"configured_order": ["fixture-extractor"]
}
})),
_ => None,
}
}
fn filesystem_artifact_fixture(schema: &str) -> Option<serde_json::Value> {
match schema {
"bcode.filesystem.read" => Some(serde_json::json!({
"path": "/tmp/fixture.rs",
"start_line": 1,
"end_line": 1,
"total_lines": 1,
"returned_bytes": 21,
"total_bytes": 21,
"truncated": false,
"contents": "fixture file contents"
})),
"bcode.filesystem.image" => Some(serde_json::json!({
"path": "/tmp/fixture.png",
"mime_type": "image/png",
"width": 640,
"height": 480,
"byte_len": 1024
})),
"bcode.filesystem.change" => Some(serde_json::json!({
"tool_name": "filesystem.edit",
"summary": "fixture change",
"path": "/tmp/fixture.txt",
"old_text": "old fixture",
"new_text": "new fixture",
"start_line": 1
})),
"bcode.filesystem.exists" => Some(serde_json::json!({
"exists": true
})),
"bcode.filesystem.list" => Some(serde_json::json!({
"entries": [{"path": "/tmp/fixture.txt", "kind": "file"}],
"backend": "fixture-backend",
"timed_out": false,
"partial": false,
"visited_entries": 1,
"message": "fixture message"
})),
"bcode.filesystem.find" => Some(serde_json::json!({
"paths": ["/tmp/fixture.txt"],
"backend": "fixture-backend",
"timed_out": false,
"partial": false,
"visited_entries": 1,
"message": "fixture message"
})),
"bcode.filesystem.grep" => Some(serde_json::json!({
"matches": [{"path": "/tmp/fixture.txt", "line_number": 1, "line": "fixture match"}],
"backend": "fixture-backend",
"timed_out": false,
"partial": false,
"visited_entries": 1,
"message": "fixture message"
})),
"bcode.filesystem.stat" => Some(serde_json::json!({
"exists": true,
"kind": "file",
"len": 128
})),
_ => filesystem_artifact_file_fixture(schema),
}
}
fn filesystem_artifact_file_fixture(schema: &str) -> Option<serde_json::Value> {
match schema {
"bcode.filesystem.artifact.metadata" => Some(serde_json::json!({
"path": "/tmp/fixture-artifact.json",
"exists": true,
"kind": "file",
"byte_len": 128,
"content_type": "application/json",
"complete": true,
"message": "fixture message"
})),
"bcode.filesystem.artifact.read" => Some(serde_json::json!({
"path": "/tmp/fixture-artifact.json",
"offset_bytes": 0,
"returned_bytes": 16,
"total_bytes": 16,
"from_end": false,
"truncated": false,
"contents": "fixture artifact"
})),
"bcode.filesystem.artifact.grep" => Some(serde_json::json!({
"path": "/tmp/fixture-artifact.json",
"matches": [{"path": "/tmp/fixture-artifact.json", "line_number": 1, "line": "fixture artifact match"}],
"total_bytes": 128,
"partial": false,
"message": "fixture message"
})),
_ => None,
}
}
fn ocr_artifact_fixture(schema: &str) -> Option<serde_json::Value> {
match schema {
"bcode.ocr.extract_result" => Some(serde_json::json!({
"text": "fixture OCR text",
"source": {
"path": "/tmp/fixture.png",
"url": null
},
"engine": "tesseract",
"language": "eng",
"truncated": false,
"text_bytes": 16,
"full_text_bytes": 16
})),
"bcode.ocr.status" => Some(serde_json::json!({
"extract": {
"available": true,
"default_engine": "tesseract",
"engines": [{
"name": "tesseract",
"available": true,
"version": "fixture-version",
"quality": "fixture-quality"
}]
}
})),
_ => None,
}
}
fn web_and_worktree_artifact_fixture(schema: &str) -> Option<serde_json::Value> {
match schema {
"bcode.shell.run" => Some(serde_json::json!({
"mode": "terminal",
"exit_code": 0,
"timed_out": false,
"cancelled": false,
"duration_ms": 125,
"output_tail": "fixture shell output",
"output_truncated": false,
"output_bytes": 20,
"retained_output_bytes": 20,
"columns": 80,
"rows": 24,
"format_commands": true
})),
"bcode.git.clone_result" => Some(serde_json::json!({
"host": "github.com",
"owner": "fixture-owner",
"repo": "fixture-repo",
"clone_url": "https://github.com/fixture-owner/fixture-repo.git",
"path": "/tmp/fixture-repo",
"already_exists": false
})),
"bcode.web-search.search_results" => Some(serde_json::json!({
"query": "fixture search",
"provider": "fixture-provider",
"results": [{
"title": "fixture result",
"url": "https://example.com/fixture",
"snippet": "fixture snippet"
}],
"partial": false,
"message": "fixture message"
})),
"bcode.web-search.fetch_result" => Some(serde_json::json!({
"url": "https://example.com/fixture",
"final_url": "https://example.com/fixture-final",
"status": 200,
"title": "fixture page",
"content_type": "text/html",
"content_format": "markdown",
"rendered": true,
"truncated": false,
"markdown": "fixture body"
})),
"bcode.question.outcome" => Some(serde_json::json!({
"status": "answered",
"questions": [{
"question_index": 0,
"header": "Choice",
"question": "Choose one",
"status": "answered",
"selected": [{"label": "Fixture", "value": "fixture"}],
"custom": null,
"required": true
}]
})),
"bcode.web-search.status" => Some(serde_json::json!({
"search": {"available": true, "provider": "fixture", "quality": "native"},
"fetch": {"available": true, "rendered_fetch": true, "max_bytes": 1024}
})),
"bcode.web-search.inspect_result" => Some(serde_json::json!({
"url": "https://example.com",
"kind": "web_page",
"recommended_tool": "web_fetch",
"recommended_action": "Fetch the page",
"notes": ["Fixture note"]
})),
_ => worktree_artifact_fixture(schema),
}
}
fn worktree_artifact_fixture(schema: &str) -> Option<serde_json::Value> {
match schema {
"bcode.worktree.list" => Some(serde_json::json!({
"main_root": "/tmp/fixture-repo",
"worktrees": [{
"path": "/tmp/fixture-worktree",
"is_main": false,
"branch": "fixture-branch",
"commit": "abc1234"
}]
})),
"bcode.worktree.create_result" => Some(serde_json::json!({
"repo_root": "/tmp/fixture-repo",
"path": "/tmp/fixture-worktree",
"branch": "fixture-branch",
"created_branch": true,
"setup_applied": false
})),
"bcode.worktree.remove_result" => Some(serde_json::json!({
"path": "/tmp/fixture-worktree"
})),
_ => None,
}
}
#[test]
fn runtime_work_is_presented_from_runtime_state_outside_transcript() {
let work = |id: &str| RuntimeWorkView {
work_id: WorkId::new(id),
kind: bcode_session_models::RuntimeWorkKind::Tool,
label: format!("work {id}"),
status: RuntimeWorkStatus::Running,
cancellable: false,
message: None,
completed_units: None,
total_units: None,
updated_at_ms: Some(1),
};
let mut snapshot = SessionViewSnapshot::empty();
snapshot.runtime_work = vec![work("first"), work("second")];
let runtime_work = unrepresented_runtime_work(&snapshot);
assert_eq!(runtime_work, snapshot.runtime_work);
assert!(snapshot.transcript.items.is_empty());
}
#[test]
fn active_interaction_controls_replace_only_matching_pending_timeline_summary() {
let interaction = |id: &str, resolved: bool| InteractionViewSummary {
producer_id: None,
exchange_schema: None,
exchange_schema_version: None,
interaction_id: id.to_owned(),
kind: "fixture.interaction".to_owned(),
tool_call_id: Some(format!("call-{id}")),
title: Some(format!("Interaction {id}")),
required: true,
snapshot: Some(serde_json::json!({"state": "pending"})),
state: bcode_session_view_models::InteractionViewState::Pending,
status_detail: None,
resolved,
resolution: resolved.then(|| bcode_session_models::ToolExchangeResolution::Responded {
payload: serde_json::json!({"status": "answered"}),
}),
};
let active = interaction("matching", false);
let matching = transcript_fixture_item(
"interaction:matching",
false,
TranscriptViewItemKind::Interaction {
interaction: active.clone(),
},
);
let unmatched = transcript_fixture_item(
"interaction:unmatched",
false,
TranscriptViewItemKind::Interaction {
interaction: interaction("unmatched", false),
},
);
let resolved = transcript_fixture_item(
"interaction:resolved",
false,
TranscriptViewItemKind::Interaction {
interaction: interaction("matching", true),
},
);
assert!(is_active_interaction_summary(&matching, &[active]));
assert!(!is_active_interaction_summary(&unmatched, &[]));
assert!(!is_active_interaction_summary(&resolved, &[]));
}
#[test]
fn direct_text_and_malformed_json_tool_results_are_bounded() {
let sentinel = "END-OF-OVERSIZED-RESULT";
let oversized = format!("{}{sentinel}", "x".repeat(40_000));
let text = render_tool_result(&ToolResultView::Text {
text: oversized.clone(),
});
let malformed = render_tool_result(&ToolResultView::Json {
value: format!("{{{oversized}"),
});
let text = container_text_all(&text);
let malformed = container_text_all(&malformed);
assert!(text.contains("Text result truncated for display."));
assert!(malformed.contains("Malformed JSON result truncated for display."));
assert!(!text.contains(sentinel));
assert!(!malformed.contains(sentinel));
}
#[test]
fn generic_json_fallback_is_bounded_and_marks_truncation() {
let sentinel = "j".repeat(40_000);
let containers = json_panel(
"developer payload",
&serde_json::json!({"sentinel": sentinel}),
);
let mut text = String::new();
for container in &containers {
container_text(container, &mut text);
}
assert!(text.contains("developer payload"));
assert!(text.contains("Structured details truncated for display."));
assert!(text.matches('j').count() <= 32_000);
}
#[test]
fn unknown_artifact_and_text_json_results_keep_readable_fallbacks() {
let unknown = ToolResultView::Artifact {
artifact: ToolArtifactView::from(ToolArtifact {
artifact_id: "unknown-artifact".to_owned(),
producer_plugin_id: "example.plugin".to_owned(),
schema: "example.unknown".to_owned(),
schema_version: 9,
tool_call_id: Some("call-unknown".to_owned()),
title: Some("Unknown result".to_owned()),
metadata: serde_json::json!({"sentinel": "preserved"}),
refs: Vec::new(),
}),
};
let text = ToolResultView::Text {
text: "plain output".to_owned(),
};
let json = ToolResultView::Json {
value: serde_json::json!({"answer": 42}).to_string(),
};
let malformed = ToolResultView::Json {
value: "{malformed".to_owned(),
};
let unknown = format!("{:?}", render_tool_result(&unknown));
let text = format!("{:?}", render_tool_result(&text));
let json = format!("{:?}", render_tool_result(&json));
let malformed = format!("{:?}", render_tool_result(&malformed));
assert!(unknown.contains("artifact details"));
assert!(unknown.contains("preserved"));
assert!(text.contains("plain output"));
assert!(!text.contains("semantic result"));
assert!(json.contains("result details"));
assert!(json.contains("answer"));
assert!(malformed.contains("{malformed"));
}
#[test]
fn shell_result_adapter_covers_terminal_captured_error_and_truncation() {
let render = |metadata| {
render_tool_result(&ToolResultView::Artifact {
artifact: ToolArtifactView::from(ToolArtifact {
artifact_id: "shell-edge".to_owned(),
producer_plugin_id: "bcode.shell".to_owned(),
schema: "bcode.shell.run".to_owned(),
schema_version: 1,
tool_call_id: Some("call-shell".to_owned()),
title: Some("Shell run".to_owned()),
metadata,
refs: Vec::new(),
}),
})
};
let terminal = render(serde_json::json!({
"mode": "terminal",
"exit_code": 0,
"timed_out": false,
"cancelled": false,
"duration_ms": 1234,
"output_tail": "terminal output",
"output_truncated": false,
"output_bytes": 15,
"retained_output_bytes": 15,
"columns": 100,
"rows": 30,
"format_commands": true
}));
let captured = render(serde_json::json!({
"mode": "captured",
"exit_code": 2,
"timed_out": false,
"cancelled": false,
"duration_ms": 50,
"stdout": "captured stdout",
"stderr": "e".repeat(33_000),
"stdout_truncated": false,
"stderr_truncated": true,
"stdout_bytes": 15,
"stderr_bytes": 33_000
}));
let timed_out = render(serde_json::json!({
"mode": "terminal",
"exit_code": null,
"timed_out": true,
"cancelled": false,
"output_tail": "timeout output",
"output_truncated": true,
"columns": 80,
"rows": 24
}));
let text = |containers: &hyperchad::template::Containers| {
let mut text = String::new();
for container in containers {
container_text(container, &mut text);
}
text
};
let terminal = text(&terminal);
let captured = text(&captured);
let timed_out = text(&timed_out);
assert!(terminal.contains("exit 0"));
assert!(terminal.contains("1234 ms"));
assert!(terminal.contains("100x30"));
assert!(terminal.contains("terminal output"));
assert!(captured.contains("exit 2"));
assert!(captured.contains("captured stdout"));
assert!(captured.contains("stderr"));
assert!(captured.contains("Shell output was truncated by the producer."));
assert!(captured.contains("Shell output truncated for display."));
assert!(timed_out.contains("timed out"));
assert!(timed_out.contains("timeout output"));
}
#[test]
fn web_search_result_adapter_handles_empty_partial_and_long_results() {
let render = |metadata| {
render_tool_result(&ToolResultView::Artifact {
artifact: ToolArtifactView::from(ToolArtifact {
artifact_id: "web-search-edge".to_owned(),
producer_plugin_id: "bcode.web-search".to_owned(),
schema: "bcode.web-search.search_results".to_owned(),
schema_version: 1,
tool_call_id: Some("call-web-search-edge".to_owned()),
title: Some("Search results".to_owned()),
metadata,
refs: Vec::new(),
}),
})
};
let empty = format!(
"{:?}",
render(serde_json::json!({
"query": "nothing",
"provider": "fixture-provider",
"results": [],
"partial": false
}))
);
let partial = format!(
"{:?}",
render(serde_json::json!({
"query": "partial",
"provider": "fixture-provider",
"results": [{
"title": "Long result",
"url": "https://example.com/long",
"snippet": "s".repeat(2_100)
}],
"partial": true,
"message": "provider returned a partial page"
}))
);
assert!(empty.contains("No search results."));
assert!(empty.contains("fixture-provider"));
assert!(partial.contains("Long result"));
assert!(partial.contains("https://example.com/long"));
assert!(partial.contains("partial results"));
assert!(partial.contains("provider returned a partial page"));
assert!(!partial.contains(&"s".repeat(2_100)));
assert!(partial.contains('…'));
}
#[test]
fn web_search_result_adapter_renders_results_without_redundant_fallback() {
let artifact = ToolArtifactView::from(ToolArtifact {
artifact_id: "web-search-result".to_owned(),
producer_plugin_id: "bcode.web-search".to_owned(),
schema: "bcode.web-search.search_results".to_owned(),
schema_version: 1,
tool_call_id: Some("call-web-search".to_owned()),
title: Some("Search results".to_owned()),
metadata: serde_json::json!({
"query": "rust tui web renderer",
"provider": "brave",
"results": [{
"title": "Renderer Neutral",
"url": "https://example.com/renderer",
"snippet": "A renderer-neutral search result"
}],
"partial": false,
"message": "ok"
}),
refs: Vec::new(),
});
let result = ToolResultView::Artifact { artifact };
let rendered = format!("{:?}", render_tool_result(&result));
assert!(rendered.contains("rust tui web renderer"));
assert!(rendered.contains("Renderer Neutral"));
assert!(rendered.contains("https://example.com/renderer"));
assert!(!rendered.contains("semantic result"));
assert!(!rendered.contains("artifact details"));
}
#[test]
fn outbound_web_url_policy_only_links_clean_http_and_https_urls() {
for accepted in [
"https://example.com/path?q=1#fragment",
"http://localhost:8080/path",
] {
assert_eq!(super::adapters::safe_web_url(accepted), Some(accepted));
}
for rejected in [
"javascript:alert(1)",
"data:text/html,unsafe",
"file:///tmp/private",
"HTTPS://example.com",
" https://example.com",
"https://",
"https://example.com/unsafe path",
"https://example.com\\@attacker.example",
"https://example.com\nattacker.example",
] {
assert_eq!(super::adapters::safe_web_url(rejected), None);
}
}
#[test]
fn web_fetch_result_adapter_bounds_markdown_and_handles_empty_unsafe_sources() {
let render = |metadata| {
render_tool_result(&ToolResultView::Artifact {
artifact: ToolArtifactView::from(ToolArtifact {
artifact_id: "web-fetch-edge".to_owned(),
producer_plugin_id: "bcode.web-search".to_owned(),
schema: "bcode.web-search.fetch_result".to_owned(),
schema_version: 1,
tool_call_id: Some("call-web-fetch-edge".to_owned()),
title: Some("Fetched page".to_owned()),
metadata,
refs: Vec::new(),
}),
})
};
let bounded = format!(
"{:?}",
render(serde_json::json!({
"url": "https://example.com/source",
"final_url": "https://example.com/final",
"status": 200,
"content_type": "text/html",
"content_format": "markdown",
"rendered": true,
"truncated": true,
"markdown": format!("# Extracted heading\n\n{}", "m".repeat(33_000))
}))
);
let empty = format!(
"{:?}",
render(serde_json::json!({
"url": "javascript:alert(1)",
"status": 204,
"content_format": "text",
"truncated": false
}))
);
assert!(bounded.contains("Extracted heading"));
assert!(bounded.contains("https://example.com/final"));
assert!(bounded.contains("source: "));
assert!(bounded.contains("https://example.com/source"));
assert!(bounded.contains("Source content was truncated."));
assert!(bounded.contains("Fetched content truncated for display."));
assert!(!bounded.contains(&format!("# Extracted heading\n\n{}", "m".repeat(33_000))));
assert!(empty.contains("javascript:alert(1)"));
assert!(!empty.contains("href: Some(\"javascript:alert(1)\")"));
assert!(empty.contains("No extracted content was returned."));
}
#[test]
fn web_fetch_result_adapter_renders_metadata_preview_without_redundant_fallback() {
let artifact = ToolArtifactView::from(ToolArtifact {
artifact_id: "web-fetch-result".to_owned(),
producer_plugin_id: "bcode.web-search".to_owned(),
schema: "bcode.web-search.fetch_result".to_owned(),
schema_version: 1,
tool_call_id: Some("call-web-fetch".to_owned()),
title: Some("Fetched page".to_owned()),
metadata: serde_json::json!({
"url": "https://example.com/original",
"final_url": "https://example.com/final",
"status": 200,
"title": "Example page",
"content_type": "text/html",
"content_format": "markdown",
"rendered": true,
"truncated": false,
"markdown": "# Sentinel preview"
}),
refs: Vec::new(),
});
let result = ToolResultView::Artifact { artifact };
let rendered = format!("{:?}", render_tool_result(&result));
assert!(rendered.contains("Example page"));
assert!(rendered.contains("https://example.com/final"));
assert!(rendered.contains("Sentinel preview"));
assert!(!rendered.contains("semantic result"));
assert!(!rendered.contains("artifact details"));
}
#[test]
fn home_renders_catalog_and_action_notices_semantically() {
let mut snapshot = SessionViewSnapshot::empty();
snapshot.catalog_status = bcode_session_view_models::SessionCatalogViewStatus::Degraded(
"Some session sources need attention.".to_owned(),
);
snapshot.notice = Some(bcode_session_view_models::SessionViewNotice {
level: bcode_session_view_models::SessionViewNoticeLevel::Error,
message: "The action could not be completed.".to_owned(),
});
let rendered = format!(
"{:?}",
home(&snapshot, &[], &crate::context::StaticPresentationContext)
);
assert!(rendered.contains("Session catalog"));
assert!(rendered.contains("Action failed"));
assert!(rendered.contains("Some sessions could not be loaded."));
assert!(!rendered.contains("Some session sources need attention."));
assert!(rendered.contains("The action could not be completed."));
}
#[test]
fn connection_states_render_clear_textual_status() {
let cases = [
(
bcode_session_view_models::SessionConnectionViewStatus::Disconnected,
"Disconnected",
),
(
bcode_session_view_models::SessionConnectionViewStatus::Connected,
"Connected · no active session",
),
(
bcode_session_view_models::SessionConnectionViewStatus::Attached,
"Connected · session attached",
),
(
bcode_session_view_models::SessionConnectionViewStatus::Reconnecting,
"Reconnecting to session…",
),
(
bcode_session_view_models::SessionConnectionViewStatus::Resyncing,
"Refreshing session state…",
),
(
bcode_session_view_models::SessionConnectionViewStatus::Error(
"Session connection needs attention.".to_owned(),
),
"Session unavailable",
),
];
for (status, expected) in cases {
let mut snapshot = SessionViewSnapshot::empty();
snapshot.connection_status = status;
let rendered = format!(
"{:?}",
home(&snapshot, &[], &crate::context::StaticPresentationContext)
);
assert!(
rendered.contains(expected),
"missing {expected}: {rendered}"
);
}
}
#[test]
fn transcript_empty_state_distinguishes_unattached_and_empty_session() {
let unattached = format!(
"{:?}",
home(
&SessionViewSnapshot::empty(),
&[],
&crate::context::StaticPresentationContext
)
);
let mut attached = SessionViewSnapshot::empty();
attached.session_id = Some(bcode_session_models::SessionId::new());
attached.connection_status = bcode_session_view_models::SessionConnectionViewStatus::Attached;
let attached = format!(
"{:?}",
home(&attached, &[], &crate::context::StaticPresentationContext)
);
assert!(unattached.contains("Attach or create a session to begin."));
assert!(attached.contains("no conversation entries in the current history view"));
}
#[test]
fn catalog_loading_degraded_and_error_states_render_semantic_language() {
let cases = [
(
bcode_session_view_models::SessionCatalogViewStatus::NotStarted,
"Session discovery has not started.",
),
(
bcode_session_view_models::SessionCatalogViewStatus::Loading,
"Loading available sessions…",
),
(
bcode_session_view_models::SessionCatalogViewStatus::Degraded(
"Some session sources are temporarily unavailable.".to_owned(),
),
"Some sessions could not be loaded. Available sessions remain usable; repair damaged sessions to restore the full list.",
),
(
bcode_session_view_models::SessionCatalogViewStatus::Failed(
"Session discovery needs attention.".to_owned(),
),
"Sessions could not be loaded. Restart Bcode or run session repair before trying again.",
),
];
for (status, expected) in cases {
let mut snapshot = SessionViewSnapshot::empty();
snapshot.catalog_status = status;
let rendered = format!(
"{:?}",
home(&snapshot, &[], &crate::context::StaticPresentationContext)
);
assert!(rendered.contains("Session catalog"));
assert!(
rendered.contains(expected),
"missing {expected}: {rendered}"
);
}
let mut loaded = SessionViewSnapshot::empty();
loaded.catalog_status = bcode_session_view_models::SessionCatalogViewStatus::Loaded;
let rendered = format!(
"{:?}",
home(&loaded, &[], &crate::context::StaticPresentationContext)
);
assert!(!rendered.contains("Session catalog"));
}
#[test]
fn session_navigation_surfaces_catalog_degraded_and_error_states() {
let cases = [
(
bcode_session_view_models::SessionCatalogViewStatus::Loading,
"Updating session list…",
),
(
bcode_session_view_models::SessionCatalogViewStatus::Degraded("partial".to_owned()),
"Session list is incomplete.",
),
(
bcode_session_view_models::SessionCatalogViewStatus::Failed("failed".to_owned()),
"Session list is unavailable.",
),
];
for (status, expected) in cases {
let rendered = format!(
"{:?}",
session_navigation(
&[],
None,
&status,
&crate::context::StaticPresentationContext
)
);
assert!(rendered.contains(expected));
assert!(rendered.contains("No sessions loaded yet."));
}
}
#[test]
fn connection_recovery_states_explain_last_view_and_authoritative_refresh() {
let cases = [
(
bcode_session_view_models::SessionConnectionViewStatus::Disconnected,
"Live session updates are unavailable.",
),
(
bcode_session_view_models::SessionConnectionViewStatus::Reconnecting,
"Showing the last available session view while live updates reconnect.",
),
(
bcode_session_view_models::SessionConnectionViewStatus::Resyncing,
"A complete authoritative session view is being requested.",
),
(
bcode_session_view_models::SessionConnectionViewStatus::Error(
"Session failed.".to_owned(),
),
"The session could not be refreshed. Try reconnecting or restart Bcode if the problem continues.",
),
];
for (status, expected) in cases {
let mut snapshot = SessionViewSnapshot::empty();
snapshot.connection_status = status;
let rendered = format!(
"{:?}",
home(&snapshot, &[], &crate::context::StaticPresentationContext)
);
assert!(
rendered.contains(expected),
"missing {expected}: {rendered}"
);
assert!(!rendered.contains("Session failed."));
assert!(!rendered.contains("IPC transport"));
}
}