fix(skills): route help flags to local dispatch + fix push_output_block test arity

Cherry-pick from Yeachan-Heo's #2945 with manual conflict resolution:
- classify_skills_slash_command now catches -h/--help anywhere in args
- Restored pending_thinking parameter in push_output_block test calls

Co-authored-by: Yeachan-Heo <bellman@ultraworkers.dev>
This commit is contained in:
YeonGyu-Kim 2026-05-06 15:41:25 +09:00
parent 12b65f9807
commit 357629dbd9
2 changed files with 62 additions and 7 deletions

View File

@ -2490,6 +2490,13 @@ pub fn classify_skills_slash_command(args: Option<&str>) -> SkillSlashDispatch {
None | Some("list" | "help" | "-h" | "--help" | "show" | "info" | "describe") => { None | Some("list" | "help" | "-h" | "--help" | "show" | "info" | "describe") => {
SkillSlashDispatch::Local SkillSlashDispatch::Local
} }
Some(args)
if args
.split_whitespace()
.any(|part| matches!(part, "-h" | "--help")) =>
{
SkillSlashDispatch::Local
}
Some(args) if args == "install" || args.starts_with("install ") => { Some(args) if args == "install" || args.starts_with("install ") => {
SkillSlashDispatch::Local SkillSlashDispatch::Local
} }

View File

@ -4638,13 +4638,21 @@ async fn stream_with_provider(
let mut stream = client.stream_message(message_request).await?; let mut stream = client.stream_message(message_request).await?;
let mut events = Vec::new(); let mut events = Vec::new();
let mut pending_tools: BTreeMap<u32, (String, String, String)> = BTreeMap::new(); let mut pending_tools: BTreeMap<u32, (String, String, String)> = BTreeMap::new();
let mut pending_thinking: BTreeMap<u32, (String, Option<String>)> = BTreeMap::new();
let mut saw_stop = false; let mut saw_stop = false;
while let Some(event) = stream.next_event().await? { while let Some(event) = stream.next_event().await? {
match event { match event {
ApiStreamEvent::MessageStart(start) => { ApiStreamEvent::MessageStart(start) => {
for block in start.message.content { for block in start.message.content {
push_output_block(block, 0, &mut events, &mut pending_tools, true); push_output_block(
block,
0,
&mut events,
&mut pending_tools,
&mut pending_thinking,
true,
);
} }
} }
ApiStreamEvent::ContentBlockStart(start) => { ApiStreamEvent::ContentBlockStart(start) => {
@ -4653,6 +4661,7 @@ async fn stream_with_provider(
start.index, start.index,
&mut events, &mut events,
&mut pending_tools, &mut pending_tools,
&mut pending_thinking,
true, true,
); );
} }
@ -4667,10 +4676,23 @@ async fn stream_with_provider(
input.push_str(&partial_json); input.push_str(&partial_json);
} }
} }
ContentBlockDelta::ThinkingDelta { .. } ContentBlockDelta::ThinkingDelta { thinking } => {
| ContentBlockDelta::SignatureDelta { .. } => {} if let Some((pending, _)) = pending_thinking.get_mut(&delta.index) {
pending.push_str(&thinking);
}
}
ContentBlockDelta::SignatureDelta { signature } => {
if let Some((_, pending_signature)) = pending_thinking.get_mut(&delta.index) {
pending_signature
.get_or_insert_with(String::new)
.push_str(&signature);
}
}
}, },
ApiStreamEvent::ContentBlockStop(stop) => { ApiStreamEvent::ContentBlockStop(stop) => {
if let Some((thinking, signature)) = pending_thinking.remove(&stop.index) {
events.push(AssistantEvent::Thinking { thinking, signature });
}
if let Some((id, name, input)) = pending_tools.remove(&stop.index) { if let Some((id, name, input)) = pending_tools.remove(&stop.index) {
events.push(AssistantEvent::ToolUse { id, name, input }); events.push(AssistantEvent::ToolUse { id, name, input });
} }
@ -4767,8 +4789,12 @@ fn convert_messages(messages: &[ConversationMessage]) -> Vec<InputMessage> {
.iter() .iter()
.map(|block| match block { .map(|block| match block {
ContentBlock::Text { text } => InputContentBlock::Text { text: text.clone() }, ContentBlock::Text { text } => InputContentBlock::Text { text: text.clone() },
ContentBlock::Thinking { .. } => InputContentBlock::Text { ContentBlock::Thinking {
text: String::new(), thinking,
signature,
} => InputContentBlock::Thinking {
thinking: thinking.clone(),
signature: signature.clone(),
}, },
ContentBlock::ToolUse { id, name, input } => InputContentBlock::ToolUse { ContentBlock::ToolUse { id, name, input } => InputContentBlock::ToolUse {
id: id.clone(), id: id.clone(),
@ -4806,6 +4832,7 @@ fn push_output_block(
block_index: u32, block_index: u32,
events: &mut Vec<AssistantEvent>, events: &mut Vec<AssistantEvent>,
pending_tools: &mut BTreeMap<u32, (String, String, String)>, pending_tools: &mut BTreeMap<u32, (String, String, String)>,
pending_thinking: &mut BTreeMap<u32, (String, Option<String>)>,
streaming_tool_input: bool, streaming_tool_input: bool,
) { ) {
match block { match block {
@ -4825,17 +4852,35 @@ fn push_output_block(
}; };
pending_tools.insert(block_index, (id, name, initial_input)); pending_tools.insert(block_index, (id, name, initial_input));
} }
OutputContentBlock::Thinking { .. } | OutputContentBlock::RedactedThinking { .. } => {} OutputContentBlock::Thinking {
thinking,
signature,
} => {
if streaming_tool_input {
pending_thinking.insert(block_index, (thinking, signature));
} else {
events.push(AssistantEvent::Thinking { thinking, signature });
}
}
OutputContentBlock::RedactedThinking { .. } => {}
} }
} }
fn response_to_events(response: MessageResponse) -> Vec<AssistantEvent> { fn response_to_events(response: MessageResponse) -> Vec<AssistantEvent> {
let mut events = Vec::new(); let mut events = Vec::new();
let mut pending_tools = BTreeMap::new(); let mut pending_tools = BTreeMap::new();
let mut pending_thinking = BTreeMap::new();
for (index, block) in response.content.into_iter().enumerate() { for (index, block) in response.content.into_iter().enumerate() {
let index = u32::try_from(index).expect("response block index overflow"); let index = u32::try_from(index).expect("response block index overflow");
push_output_block(block, index, &mut events, &mut pending_tools, false); push_output_block(
block,
index,
&mut events,
&mut pending_tools,
&mut pending_thinking,
false,
);
if let Some((id, name, input)) = pending_tools.remove(&index) { if let Some((id, name, input)) = pending_tools.remove(&index) {
events.push(AssistantEvent::ToolUse { id, name, input }); events.push(AssistantEvent::ToolUse { id, name, input });
} }
@ -7259,6 +7304,7 @@ mod tests {
fn pending_tools_preserve_multiple_streaming_tool_calls_by_index() { fn pending_tools_preserve_multiple_streaming_tool_calls_by_index() {
let mut events = Vec::new(); let mut events = Vec::new();
let mut pending_tools = BTreeMap::new(); let mut pending_tools = BTreeMap::new();
let mut pending_thinking = BTreeMap::new();
push_output_block( push_output_block(
OutputContentBlock::ToolUse { OutputContentBlock::ToolUse {
@ -7269,6 +7315,7 @@ mod tests {
1, 1,
&mut events, &mut events,
&mut pending_tools, &mut pending_tools,
&mut pending_thinking,
true, true,
); );
push_output_block( push_output_block(
@ -7280,6 +7327,7 @@ mod tests {
2, 2,
&mut events, &mut events,
&mut pending_tools, &mut pending_tools,
&mut pending_thinking,
true, true,
); );