The great storkit name conversion
This commit is contained in:
@@ -185,10 +185,10 @@ fn run_pty_session(
|
||||
// are emitted and tool-start activity signals never fire.
|
||||
cmd.arg("--include-partial-messages");
|
||||
// Delegate permission decisions to the MCP prompt_permission tool.
|
||||
// Claude Code will call this tool via the story-kit MCP server when
|
||||
// Claude Code will call this tool via the storkit MCP server when
|
||||
// a tool requires user approval, instead of using PTY stdin/stdout.
|
||||
cmd.arg("--permission-prompt-tool");
|
||||
cmd.arg("mcp__story-kit__prompt_permission");
|
||||
cmd.arg("mcp__storkit__prompt_permission");
|
||||
// Note: --system is not a valid Claude Code CLI flag. System-level
|
||||
// instructions (like bot name) are prepended to the user prompt instead.
|
||||
cmd.cwd(cwd);
|
||||
@@ -198,7 +198,7 @@ fn run_pty_session(
|
||||
cmd.env("CLAUDECODE", "");
|
||||
|
||||
slog!(
|
||||
"[pty-debug] Spawning: claude -p \"{}\" {} --output-format stream-json --verbose --include-partial-messages --permission-prompt-tool mcp__story-kit__prompt_permission",
|
||||
"[pty-debug] Spawning: claude -p \"{}\" {} --output-format stream-json --verbose --include-partial-messages --permission-prompt-tool mcp__storkit__prompt_permission",
|
||||
user_message,
|
||||
resume_session_id
|
||||
.map(|s| format!("--resume {s}"))
|
||||
@@ -210,10 +210,7 @@ fn run_pty_session(
|
||||
.spawn_command(cmd)
|
||||
.map_err(|e| format!("Failed to spawn claude: {e}"))?;
|
||||
|
||||
slog!(
|
||||
"[pty-debug] Process spawned, pid: {:?}",
|
||||
child.process_id()
|
||||
);
|
||||
slog!("[pty-debug] Process spawned, pid: {:?}", child.process_id());
|
||||
drop(pair.slave);
|
||||
|
||||
let reader = pair
|
||||
@@ -274,7 +271,14 @@ fn run_pty_session(
|
||||
|
||||
// Try to parse as JSON
|
||||
if let Ok(json) = serde_json::from_str::<serde_json::Value>(trimmed)
|
||||
&& process_json_event(&json, &token_tx, &thinking_tx, &activity_tx, &msg_tx, &mut sid_tx)
|
||||
&& process_json_event(
|
||||
&json,
|
||||
&token_tx,
|
||||
&thinking_tx,
|
||||
&activity_tx,
|
||||
&msg_tx,
|
||||
&mut sid_tx,
|
||||
)
|
||||
{
|
||||
got_result = true;
|
||||
}
|
||||
@@ -462,10 +466,7 @@ fn parse_assistant_message(
|
||||
///
|
||||
/// Claude Code injects tool results into the conversation as `user` role
|
||||
/// messages. Each `tool_result` block becomes a separate `Message { role: Tool }`.
|
||||
fn parse_tool_results(
|
||||
content: &[serde_json::Value],
|
||||
msg_tx: &std::sync::mpsc::Sender<Message>,
|
||||
) {
|
||||
fn parse_tool_results(content: &[serde_json::Value], msg_tx: &std::sync::mpsc::Sender<Message>) {
|
||||
for block in content {
|
||||
if block.get("type").and_then(|t| t.as_str()) != Some("tool_result") {
|
||||
continue;
|
||||
@@ -484,7 +485,9 @@ fn parse_tool_results(
|
||||
arr.iter()
|
||||
.filter_map(|b| {
|
||||
if b.get("type").and_then(|t| t.as_str()) == Some("text") {
|
||||
b.get("text").and_then(|t| t.as_str()).map(|s| s.to_string())
|
||||
b.get("text")
|
||||
.and_then(|t| t.as_str())
|
||||
.map(|s| s.to_string())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
@@ -537,9 +540,7 @@ fn handle_stream_event(
|
||||
}
|
||||
}
|
||||
"thinking_delta" => {
|
||||
if let Some(thinking) =
|
||||
delta.get("thinking").and_then(|t| t.as_str())
|
||||
{
|
||||
if let Some(thinking) = delta.get("thinking").and_then(|t| t.as_str()) {
|
||||
let _ = thinking_tx.send(thinking.to_string());
|
||||
}
|
||||
}
|
||||
@@ -566,9 +567,7 @@ mod tests {
|
||||
use super::*;
|
||||
use serde_json::json;
|
||||
|
||||
fn collect_messages(
|
||||
f: impl Fn(&std::sync::mpsc::Sender<Message>),
|
||||
) -> Vec<Message> {
|
||||
fn collect_messages(f: impl Fn(&std::sync::mpsc::Sender<Message>)) -> Vec<Message> {
|
||||
let (tx, rx) = std::sync::mpsc::channel();
|
||||
f(&tx);
|
||||
drop(tx);
|
||||
@@ -755,7 +754,10 @@ mod tests {
|
||||
}
|
||||
v
|
||||
};
|
||||
assert!(tokens.is_empty(), "thinking token leaked into token channel");
|
||||
assert!(
|
||||
tokens.is_empty(),
|
||||
"thinking token leaked into token channel"
|
||||
);
|
||||
// thinking token must appear in the dedicated thinking channel, without prefix
|
||||
let thinking: Vec<String> = {
|
||||
let mut v = vec![];
|
||||
@@ -897,7 +899,9 @@ mod tests {
|
||||
let (thi_tx, thi_rx) = tokio::sync::mpsc::unbounded_channel();
|
||||
let (act_tx, act_rx) = tokio::sync::mpsc::unbounded_channel();
|
||||
let (msg_tx, msg_rx) = std::sync::mpsc::channel();
|
||||
(tok_tx, tok_rx, thi_tx, thi_rx, act_tx, act_rx, msg_tx, msg_rx)
|
||||
(
|
||||
tok_tx, tok_rx, thi_tx, thi_rx, act_tx, act_rx, msg_tx, msg_rx,
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -906,7 +910,14 @@ mod tests {
|
||||
let (sid_tx, _sid_rx) = tokio::sync::oneshot::channel::<String>();
|
||||
let mut sid_tx_opt = Some(sid_tx);
|
||||
let json = json!({"type": "result", "subtype": "success"});
|
||||
assert!(process_json_event(&json, &tok_tx, &thi_tx, &act_tx, &msg_tx, &mut sid_tx_opt));
|
||||
assert!(process_json_event(
|
||||
&json,
|
||||
&tok_tx,
|
||||
&thi_tx,
|
||||
&act_tx,
|
||||
&msg_tx,
|
||||
&mut sid_tx_opt
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -914,7 +925,14 @@ mod tests {
|
||||
let (tok_tx, _tok_rx, thi_tx, _thi_rx, act_tx, _act_rx, msg_tx, _msg_rx) = make_channels();
|
||||
let mut sid_tx = None::<tokio::sync::oneshot::Sender<String>>;
|
||||
let json = json!({"type": "system", "subtype": "init", "apiKeySource": "env"});
|
||||
assert!(!process_json_event(&json, &tok_tx, &thi_tx, &act_tx, &msg_tx, &mut sid_tx));
|
||||
assert!(!process_json_event(
|
||||
&json,
|
||||
&tok_tx,
|
||||
&thi_tx,
|
||||
&act_tx,
|
||||
&msg_tx,
|
||||
&mut sid_tx
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -922,7 +940,14 @@ mod tests {
|
||||
let (tok_tx, _tok_rx, thi_tx, _thi_rx, act_tx, _act_rx, msg_tx, _msg_rx) = make_channels();
|
||||
let mut sid_tx = None::<tokio::sync::oneshot::Sender<String>>;
|
||||
let json = json!({"type": "rate_limit_event"});
|
||||
assert!(!process_json_event(&json, &tok_tx, &thi_tx, &act_tx, &msg_tx, &mut sid_tx));
|
||||
assert!(!process_json_event(
|
||||
&json,
|
||||
&tok_tx,
|
||||
&thi_tx,
|
||||
&act_tx,
|
||||
&msg_tx,
|
||||
&mut sid_tx
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -930,7 +955,14 @@ mod tests {
|
||||
let (tok_tx, _tok_rx, thi_tx, _thi_rx, act_tx, _act_rx, msg_tx, _msg_rx) = make_channels();
|
||||
let mut sid_tx = None::<tokio::sync::oneshot::Sender<String>>;
|
||||
let json = json!({"type": "some_future_event"});
|
||||
assert!(!process_json_event(&json, &tok_tx, &thi_tx, &act_tx, &msg_tx, &mut sid_tx));
|
||||
assert!(!process_json_event(
|
||||
&json,
|
||||
&tok_tx,
|
||||
&thi_tx,
|
||||
&act_tx,
|
||||
&msg_tx,
|
||||
&mut sid_tx
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -938,7 +970,14 @@ mod tests {
|
||||
let (tok_tx, _tok_rx, thi_tx, _thi_rx, act_tx, _act_rx, msg_tx, _msg_rx) = make_channels();
|
||||
let mut sid_tx = None::<tokio::sync::oneshot::Sender<String>>;
|
||||
let json = json!({"content": "no type field"});
|
||||
assert!(!process_json_event(&json, &tok_tx, &thi_tx, &act_tx, &msg_tx, &mut sid_tx));
|
||||
assert!(!process_json_event(
|
||||
&json,
|
||||
&tok_tx,
|
||||
&thi_tx,
|
||||
&act_tx,
|
||||
&msg_tx,
|
||||
&mut sid_tx
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -967,7 +1006,8 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn process_json_event_stream_event_forwards_token() {
|
||||
let (tok_tx, mut tok_rx, thi_tx, _thi_rx, act_tx, _act_rx, msg_tx, _msg_rx) = make_channels();
|
||||
let (tok_tx, mut tok_rx, thi_tx, _thi_rx, act_tx, _act_rx, msg_tx, _msg_rx) =
|
||||
make_channels();
|
||||
let mut sid_tx = None::<tokio::sync::oneshot::Sender<String>>;
|
||||
let json = json!({
|
||||
"type": "stream_event",
|
||||
@@ -977,7 +1017,14 @@ mod tests {
|
||||
"delta": {"type": "text_delta", "text": "word"}
|
||||
}
|
||||
});
|
||||
assert!(!process_json_event(&json, &tok_tx, &thi_tx, &act_tx, &msg_tx, &mut sid_tx));
|
||||
assert!(!process_json_event(
|
||||
&json,
|
||||
&tok_tx,
|
||||
&thi_tx,
|
||||
&act_tx,
|
||||
&msg_tx,
|
||||
&mut sid_tx
|
||||
));
|
||||
drop(tok_tx);
|
||||
let tokens: Vec<String> = {
|
||||
let mut v = vec![];
|
||||
@@ -993,7 +1040,8 @@ mod tests {
|
||||
fn process_json_event_stream_event_tool_use_fires_activity() {
|
||||
// This is the primary activity path: stream_event wrapping content_block_start
|
||||
// with a tool_use block. Requires --include-partial-messages to be enabled.
|
||||
let (tok_tx, _tok_rx, thi_tx, _thi_rx, act_tx, mut act_rx, msg_tx, _msg_rx) = make_channels();
|
||||
let (tok_tx, _tok_rx, thi_tx, _thi_rx, act_tx, mut act_rx, msg_tx, _msg_rx) =
|
||||
make_channels();
|
||||
let mut sid_tx = None::<tokio::sync::oneshot::Sender<String>>;
|
||||
let json = json!({
|
||||
"type": "stream_event",
|
||||
@@ -1004,7 +1052,14 @@ mod tests {
|
||||
"content_block": {"type": "tool_use", "id": "toolu_abc", "name": "Bash", "input": {}}
|
||||
}
|
||||
});
|
||||
assert!(!process_json_event(&json, &tok_tx, &thi_tx, &act_tx, &msg_tx, &mut sid_tx));
|
||||
assert!(!process_json_event(
|
||||
&json,
|
||||
&tok_tx,
|
||||
&thi_tx,
|
||||
&act_tx,
|
||||
&msg_tx,
|
||||
&mut sid_tx
|
||||
));
|
||||
drop(act_tx);
|
||||
let activities: Vec<String> = {
|
||||
let mut v = vec![];
|
||||
@@ -1018,7 +1073,8 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn process_json_event_assistant_with_tool_use_fires_activity() {
|
||||
let (tok_tx, _tok_rx, thi_tx, _thi_rx, act_tx, mut act_rx, msg_tx, _msg_rx) = make_channels();
|
||||
let (tok_tx, _tok_rx, thi_tx, _thi_rx, act_tx, mut act_rx, msg_tx, _msg_rx) =
|
||||
make_channels();
|
||||
let mut sid_tx = None::<tokio::sync::oneshot::Sender<String>>;
|
||||
let json = json!({
|
||||
"type": "assistant",
|
||||
@@ -1029,7 +1085,14 @@ mod tests {
|
||||
]
|
||||
}
|
||||
});
|
||||
assert!(!process_json_event(&json, &tok_tx, &thi_tx, &act_tx, &msg_tx, &mut sid_tx));
|
||||
assert!(!process_json_event(
|
||||
&json,
|
||||
&tok_tx,
|
||||
&thi_tx,
|
||||
&act_tx,
|
||||
&msg_tx,
|
||||
&mut sid_tx
|
||||
));
|
||||
drop(act_tx);
|
||||
let activities: Vec<String> = {
|
||||
let mut v = vec![];
|
||||
@@ -1043,7 +1106,8 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn process_json_event_assistant_with_multiple_tool_uses_fires_all_activities() {
|
||||
let (tok_tx, _tok_rx, thi_tx, _thi_rx, act_tx, mut act_rx, msg_tx, _msg_rx) = make_channels();
|
||||
let (tok_tx, _tok_rx, thi_tx, _thi_rx, act_tx, mut act_rx, msg_tx, _msg_rx) =
|
||||
make_channels();
|
||||
let mut sid_tx = None::<tokio::sync::oneshot::Sender<String>>;
|
||||
let json = json!({
|
||||
"type": "assistant",
|
||||
@@ -1054,7 +1118,14 @@ mod tests {
|
||||
]
|
||||
}
|
||||
});
|
||||
assert!(!process_json_event(&json, &tok_tx, &thi_tx, &act_tx, &msg_tx, &mut sid_tx));
|
||||
assert!(!process_json_event(
|
||||
&json,
|
||||
&tok_tx,
|
||||
&thi_tx,
|
||||
&act_tx,
|
||||
&msg_tx,
|
||||
&mut sid_tx
|
||||
));
|
||||
drop(act_tx);
|
||||
let activities: Vec<String> = {
|
||||
let mut v = vec![];
|
||||
@@ -1068,7 +1139,8 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn process_json_event_assistant_text_only_no_activity() {
|
||||
let (tok_tx, _tok_rx, thi_tx, _thi_rx, act_tx, mut act_rx, msg_tx, _msg_rx) = make_channels();
|
||||
let (tok_tx, _tok_rx, thi_tx, _thi_rx, act_tx, mut act_rx, msg_tx, _msg_rx) =
|
||||
make_channels();
|
||||
let mut sid_tx = None::<tokio::sync::oneshot::Sender<String>>;
|
||||
let json = json!({
|
||||
"type": "assistant",
|
||||
@@ -1076,7 +1148,14 @@ mod tests {
|
||||
"content": [{"type": "text", "text": "Just text, no tools."}]
|
||||
}
|
||||
});
|
||||
assert!(!process_json_event(&json, &tok_tx, &thi_tx, &act_tx, &msg_tx, &mut sid_tx));
|
||||
assert!(!process_json_event(
|
||||
&json,
|
||||
&tok_tx,
|
||||
&thi_tx,
|
||||
&act_tx,
|
||||
&msg_tx,
|
||||
&mut sid_tx
|
||||
));
|
||||
drop(act_tx);
|
||||
let activities: Vec<String> = {
|
||||
let mut v = vec![];
|
||||
@@ -1098,7 +1177,14 @@ mod tests {
|
||||
"content": [{"type": "text", "text": "Hi!"}]
|
||||
}
|
||||
});
|
||||
assert!(!process_json_event(&json, &tok_tx, &thi_tx, &act_tx, &msg_tx, &mut sid_tx));
|
||||
assert!(!process_json_event(
|
||||
&json,
|
||||
&tok_tx,
|
||||
&thi_tx,
|
||||
&act_tx,
|
||||
&msg_tx,
|
||||
&mut sid_tx
|
||||
));
|
||||
drop(msg_tx);
|
||||
let msgs: Vec<Message> = msg_rx.try_iter().collect();
|
||||
assert_eq!(msgs.len(), 1);
|
||||
@@ -1115,7 +1201,14 @@ mod tests {
|
||||
"content": [{"type": "tool_result", "tool_use_id": "tid1", "content": "done"}]
|
||||
}
|
||||
});
|
||||
assert!(!process_json_event(&json, &tok_tx, &thi_tx, &act_tx, &msg_tx, &mut sid_tx));
|
||||
assert!(!process_json_event(
|
||||
&json,
|
||||
&tok_tx,
|
||||
&thi_tx,
|
||||
&act_tx,
|
||||
&msg_tx,
|
||||
&mut sid_tx
|
||||
));
|
||||
drop(msg_tx);
|
||||
let msgs: Vec<Message> = msg_rx.try_iter().collect();
|
||||
assert_eq!(msgs.len(), 1);
|
||||
@@ -1131,7 +1224,14 @@ mod tests {
|
||||
"type": "assistant",
|
||||
"message": {"content": "not an array"}
|
||||
});
|
||||
assert!(!process_json_event(&json, &tok_tx, &thi_tx, &act_tx, &msg_tx, &mut sid_tx));
|
||||
assert!(!process_json_event(
|
||||
&json,
|
||||
&tok_tx,
|
||||
&thi_tx,
|
||||
&act_tx,
|
||||
&msg_tx,
|
||||
&mut sid_tx
|
||||
));
|
||||
drop(msg_tx);
|
||||
let msgs: Vec<Message> = msg_rx.try_iter().collect();
|
||||
assert!(msgs.is_empty());
|
||||
@@ -1142,7 +1242,14 @@ mod tests {
|
||||
let (tok_tx, _tok_rx, thi_tx, _thi_rx, act_tx, _act_rx, msg_tx, msg_rx) = make_channels();
|
||||
let mut sid_tx = None::<tokio::sync::oneshot::Sender<String>>;
|
||||
let json = json!({"type": "user", "message": {"content": null}});
|
||||
assert!(!process_json_event(&json, &tok_tx, &thi_tx, &act_tx, &msg_tx, &mut sid_tx));
|
||||
assert!(!process_json_event(
|
||||
&json,
|
||||
&tok_tx,
|
||||
&thi_tx,
|
||||
&act_tx,
|
||||
&msg_tx,
|
||||
&mut sid_tx
|
||||
));
|
||||
drop(msg_tx);
|
||||
let msgs: Vec<Message> = msg_rx.try_iter().collect();
|
||||
assert!(msgs.is_empty());
|
||||
|
||||
Reference in New Issue
Block a user