Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 17 additions & 17 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 4 additions & 4 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,10 @@ repository = "https://github.com/cppalliance/runvil-workshop"
# in. All four name one commit, because two commits would be two copies
# of every type the crates share. `version` beside `git` keeps cargo-deny's
# wildcard check quiet.
runvil = { version = "0.3.0", git = "https://github.com/cppalliance/runvil-harness", rev = "5bca6f9fd9b398d5f221a5893570b8085bc283da" }
harness-gateway-client = { version = "0.3.0", git = "https://github.com/cppalliance/runvil-harness", rev = "5bca6f9fd9b398d5f221a5893570b8085bc283da" }
plugin-web = { version = "0.3.0", git = "https://github.com/cppalliance/runvil-harness", rev = "5bca6f9fd9b398d5f221a5893570b8085bc283da" }
plugin-mcp = { version = "0.3.0", git = "https://github.com/cppalliance/runvil-harness", rev = "5bca6f9fd9b398d5f221a5893570b8085bc283da" }
runvil = { version = "0.3.0", git = "https://github.com/cppalliance/runvil-harness", rev = "7147e709f603b81786fb2d702bf50c4d9a88d62b" }
harness-gateway-client = { version = "0.3.0", git = "https://github.com/cppalliance/runvil-harness", rev = "7147e709f603b81786fb2d702bf50c4d9a88d62b" }
plugin-web = { version = "0.3.0", git = "https://github.com/cppalliance/runvil-harness", rev = "7147e709f603b81786fb2d702bf50c4d9a88d62b" }
plugin-mcp = { version = "0.3.0", git = "https://github.com/cppalliance/runvil-harness", rev = "7147e709f603b81786fb2d702bf50c4d9a88d62b" }
# The safe layer over the C Plugin contract, version 0.4, from its own
# repository, pinned by commit. Only the library Plugin crates under
# `crates/workshop/` name it, for `export_plugin!`. The commit is the one
Expand Down
2 changes: 0 additions & 2 deletions crates/workshop/agents/agents/chat.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,6 @@ models:
min_context: 32768
---

# Chat

The built-in chat agent: a pass-through between the operator and the selected model, whose one system message says where the Runvil prompting language documentation is mounted. The conversation is the section's `messages` list, retained across turns; the model is the dropdown's selection bound at run launch, so a selection change takes effect on the next run. When the conversation outgrows the model's context window, older turns are summarized so the conversation continues.

## Conversation
Expand Down
10 changes: 7 additions & 3 deletions crates/workshop/agents/tests/it/conversation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ async fn an_agent_that_fails_to_parse_reports_a_run_failure_and_records_no_run()
let conversation = table.open("unclosed");
let recorder = Arc::new(MemoryRecorder::new());
let mut errors = conversation.subscribe_errors();
let unclosed = "---\nname: unclosed\ndescription: d\nrunvil: 0\n\n# Title\n".to_owned();
let unclosed = "---\nname: unclosed\ndescription: d\nrunvil: 0\n\n## Only\n".to_owned();
let (host, _setup) = run_for(&conversation, Arc::clone(&recorder), unclosed.clone());
let parse_failure = host
.parse(AGENT)
Expand Down Expand Up @@ -152,15 +152,19 @@ async fn a_completed_report_reports_nothing_and_ends_the_conversation() {
let recorder = Arc::new(MemoryRecorder::new());
let mut errors = conversation.subscribe_errors();

ended(start(&conversation, &recorder, prompt("return 'done'"))).await;
ended(start(&conversation, &recorder, prompt("return 'finished'"))).await;

assert!(
reports(&mut errors).is_empty(),
"a completed run reports nothing"
);
assert_eq!(conversation.state(), SessionState::Closed);
let run = conversation.run_id().expect("the recorder began the run");
assert_eq!(recorder.result(run), Some(Ok("done".to_owned())));
assert_eq!(
recorder.result(run),
Some(Ok("finished".to_owned())),
"the result is the text the agent's Lua returned, where a run with no text ends as `done`"
);
assert!(table.get(conversation.id()).is_none());
}

Expand Down
2 changes: 1 addition & 1 deletion crates/workshop/agents/tests/it/support.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ pub(crate) fn prompt(lua: &str) -> String {
format!(
"---\nname: fixture\ndescription: a conversation fixture\nrunvil: 0\n\
uses:\n - user\n---\n\n\
# Fixture\n\n## Conversation\n\n```lua\n{lua}\n```\n"
## Conversation\n\n```lua\n{lua}\n```\n"
)
}

Expand Down
4 changes: 0 additions & 4 deletions crates/workshop/server/src/agents/language_guide/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -211,8 +211,6 @@ uses:
- std
---

# Sees the mapping

## Only

```lua
Expand Down Expand Up @@ -254,8 +252,6 @@ runvil: 0
uses: [bash]
---

# Shells over the mappings

## Only

```lua
Expand Down
8 changes: 4 additions & 4 deletions crates/workshop/server/src/agents/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ fn requiring(pack: &str) -> String {
format!(
"---\nname: browses\ndescription: needs {pack}\nrunvil: 0\n\
uses:\n - {pack}\n---\n\n\
# Browses\n\n## Only\n\n```lua\nreturn 'browsed'\n```\n"
## Only\n\n```lua\nreturn 'browsed'\n```\n"
)
}

Expand Down Expand Up @@ -142,7 +142,7 @@ fn mcp_file(text: &str) -> (tempfile::TempDir, std::path::PathBuf) {
#[tokio::test]
async fn every_run_holds_the_packs_the_three_plugins_add_and_the_builtins() {
let lists = "---\nname: lists\ndescription: reads its Packs\nrunvil: 0\n---\n\n\
# Lists\n\n## Only\n\n```lua\n\
## Only\n\n```lua\n\
local names = {}\n\
for _, id in ipairs({ 'web', 'subagents', 'mcp', 'std', 'user' }) do\n\
names[#names + 1] = tools.packs.get(id).name\n\
Expand Down Expand Up @@ -417,7 +417,7 @@ fn only_a_plugin_directory_with_a_shipped_library_gives_the_host_its_pack() {
/// A prompt that declares `std` and returns what `std/read` answers for
/// `/notes.md`, then what `std/write` answers.
const READS_NOTES: &str = "---\nname: reads\ndescription: reads a file\nrunvil: 0\n\
uses:\n - std\n---\n\n# Reads\n\n## Only\n\n```lua\n\
uses:\n - std\n---\n\n## Only\n\n```lua\n\
local read = tools.call('std/read', { path = '/notes.md' })\n\
local write = tools.call('std/write', { path = '/out.md', contents = 'x' })\n\
return read .. '|' .. write\n```\n";
Expand Down Expand Up @@ -480,7 +480,7 @@ async fn without_a_plugin_directory_a_prompt_requiring_a_shipped_pack_is_refused
/// A prompt that declares `bash` and returns what `bash/shell` answers
/// for a command that reads `/notes.md` and then writes a file.
const RUNS_A_COMMAND: &str = "---\nname: shells\ndescription: runs a command\nrunvil: 0\n\
uses:\n - bash\n---\n\n# Shells\n\n## Only\n\n```lua\n\
uses:\n - bash\n---\n\n## Only\n\n```lua\n\
return tools.call('bash/shell', { command = 'cat /notes.md; echo x > /out.md' })\n```\n";

#[tokio::test]
Expand Down
11 changes: 4 additions & 7 deletions crates/workshop/server/src/routes/prompts-tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,6 @@ models:
fast: {}
---

# Full

## Only

Body text.
Expand Down Expand Up @@ -119,7 +117,7 @@ async fn a_full_frontmatter_prompt_answers_every_contract_section() {

#[tokio::test]
async fn a_broken_yaml_key_answers_parse_frontmatter_with_the_line() {
let text = "---\nname: [unclosed\ndescription: x\n---\n\n# T\n\n## S\n\np\n";
let text = "---\nname: [unclosed\ndescription: x\n---\n\n## S\n\np\n";
let body = serde_json::json!({ "name": "broken", "text": text });
let (status, json) = post_contract(body).await;
assert_eq!(status, StatusCode::UNPROCESSABLE_ENTITY);
Expand Down Expand Up @@ -150,7 +148,7 @@ fn an_internal_parse_failure_names_no_prompt_line() {

#[tokio::test]
async fn a_prompt_without_args_yields_the_implicit_prose_field() {
let text = "---\nname: plain\ndescription: no args\n---\n\n# Plain\n\n## S\n\np\n";
let text = "---\nname: plain\ndescription: no args\n---\n\n## S\n\np\n";
let body = serde_json::json!({ "name": "plain", "text": text });
let (status, json) = post_contract(body).await;
assert_eq!(status, StatusCode::OK);
Expand All @@ -172,8 +170,7 @@ async fn a_prompt_without_args_yields_the_implicit_prose_field() {

#[tokio::test]
async fn a_lua_error_answers_parse_lua() {
let text =
"---\nname: badlua\ndescription: bad lua\n---\n\n# Bad\n\n## S\n\n```lua\nreturn (\n```\n";
let text = "---\nname: badlua\ndescription: bad lua\n---\n\n## S\n\n```lua\nreturn (\n```\n";
let body = serde_json::json!({ "name": "badlua", "text": text });
let (status, json) = post_contract(body).await;
assert_eq!(status, StatusCode::UNPROCESSABLE_ENTITY);
Expand All @@ -193,7 +190,7 @@ async fn the_contract_has_no_tools_section() {

#[tokio::test]
async fn the_dto_serializes_absent_declarations_as_nulls() {
let text = "---\nname: nulls\ndescription: absent keys\n---\n\n# Nulls\n\n## S\n\np\n";
let text = "---\nname: nulls\ndescription: absent keys\n---\n\n## S\n\np\n";
let body = serde_json::json!({ "name": "nulls", "text": text });
let (status, json) = post_contract(body).await;
assert_eq!(status, StatusCode::OK);
Expand Down
2 changes: 0 additions & 2 deletions crates/workshop/server/tests/it/agents.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,6 @@ uses:
- user
---

# Echo

## Conversation

```lua
Expand Down
6 changes: 2 additions & 4 deletions crates/workshop/server/tests/it/agents/lifecycle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -153,9 +153,9 @@ async fn a_program_that_returns_frames_run_ended_and_a_later_message_is_refused(
json!({
"type": "agent_event",
"index": 2,
"event": { "kind": "run_ended", "section": "Echo", "turn": 0, "content": "" },
"event": { "kind": "run_ended", "section": "echo", "turn": 0, "content": "" },
}),
"the run's end frames right after the message it returned on, under the prompt's title"
"the run's end frames right after the message it returned on, under the prompt's name"
);

answer(&mut socket, "too late").await;
Expand Down Expand Up @@ -184,8 +184,6 @@ uses:
- user
---

# Boom

## Conversation

```lua
Expand Down
2 changes: 0 additions & 2 deletions crates/workshop/server/tests/it/agents/revoke.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,6 @@ uses:
- user
---

# Roots

## Conversation

```lua
Expand Down
2 changes: 0 additions & 2 deletions crates/workshop/server/tests/it/agents/turns.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,6 @@ uses:
- web
---

# Steer

## Conversation

```lua
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -203,8 +203,8 @@ async fn gate_a_conversation_round_advertises_the_shell_after_the_file_tools() {

/// GATE 12 - web tools. The chat's `## Conversation` section offers the
/// `web` Pack, so the rounds its model loop sends advertise the
/// Pack's fetch and search tools; an offer in the H1 would not reach
/// them.
/// Pack's fetch and search tools; an offer above that heading would not
/// reach them.
#[tokio::test]
async fn gate_a_conversation_round_advertises_the_web_tools() {
let server = spawn_chat_server(&["test-model"]).await;
Expand Down
Loading