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
11 changes: 11 additions & 0 deletions Cargo.lock

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

3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[workspace]
resolver = "3"
members = ["crates/*", "crates/runvil-internal/types", "crates/runvil-internal/runtime", "crates/runvil-internal/lua", "crates/runvil-internal/prompt", "crates/runvil-internal/vfs", "crates/runvil-internal/model-client", "crates/workshop/desktop", "crates/workshop/server", "crates/workshop/server-api", "crates/workshop/gateway", "crates/workshop/gateway-discovery", "crates/workshop/menu", "crates/workshop/protocol", "crates/workshop/registry", "crates/workshop/status", "crates/workshop/support", "crates/workshop/user-state", "crates/workshop/workspace", "crates/workshop/run-log", "crates/workshop/agents", "crates/workshop/plugin-std", "crates/harness-internal/runner", "crates/harness-internal/plugin-loader", "crates/harness-internal/test-plugin"]
members = ["crates/*", "crates/runvil-internal/types", "crates/runvil-internal/runtime", "crates/runvil-internal/lua", "crates/runvil-internal/markdown", "crates/runvil-internal/prompt", "crates/runvil-internal/vfs", "crates/runvil-internal/model-client", "crates/workshop/desktop", "crates/workshop/server", "crates/workshop/server-api", "crates/workshop/gateway", "crates/workshop/gateway-discovery", "crates/workshop/menu", "crates/workshop/protocol", "crates/workshop/registry", "crates/workshop/status", "crates/workshop/support", "crates/workshop/user-state", "crates/workshop/workspace", "crates/workshop/run-log", "crates/workshop/agents", "crates/workshop/plugin-std", "crates/harness-internal/runner", "crates/harness-internal/plugin-loader", "crates/harness-internal/test-plugin"]
# crates/runvil-internal, crates/workshop, and crates/harness-internal
# are manifestless containers holding their families' private crates.
# Cargo prunes excluded subtrees from member globs, so a "crates/workshop/*"
Expand Down Expand Up @@ -49,6 +49,7 @@ harness-plugin-loader = { path = "crates/harness-internal/plugin-loader" }
plugin-web = { path = "crates/plugin-web" }
plugin-mcp = { path = "crates/plugin-mcp" }
runvil-lua = { path = "crates/runvil-internal/lua" }
runvil-markdown = { path = "crates/runvil-internal/markdown" }
runvil-model-client = { path = "crates/runvil-internal/model-client" }
runvil-prompt = { path = "crates/runvil-internal/prompt" }
runvil-vfs = { path = "crates/runvil-internal/vfs" }
Expand Down
2 changes: 1 addition & 1 deletion crates/build-xtask/src/engine_clippy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
//!
//! Clippy reads the nearest `.clippy.toml` or `clippy.toml` walking up from
//! a crate's manifest directory and never merges it with the root file. So
//! `crates/runvil-internal/clippy.toml` binds the six internal crates,
//! `crates/runvil-internal/clippy.toml` binds the seven internal crates,
//! `crates/runvil-engine/clippy.toml` and
//! `crates/runvil-plugin/clippy.toml` bind the two root crates, and each
//! repeats the root file's settings and
Expand Down
6 changes: 3 additions & 3 deletions crates/harness-internal/runner/tests/it/effect_loop-cap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,16 +47,16 @@ const PREAMBLE: &str = "---\nname: runner-cap\ndescription: caps tool calls\nrun
/// Main spawns Child and waits on a `wait` round while Child's loop asks
/// its round, then cancels Child and lingers on a second round.
const MAIN_CANCELS: &str = "## Main\n\n```lua\n\
local t = tasks.spawn('## Child')\n\
local t = tasks.spawn('../Child')\n\
models.infer('wait')\n\
tasks.cancel(t)\n\
models.infer('linger')\n\
return 'canceled'\n```\n\n";

/// Main spawns A, then B, and joins both.
const MAIN_JOINS: &str = "## Main\n\n```lua\n\
local a = tasks.spawn('## A')\n\
local b = tasks.spawn('## B')\n\
local a = tasks.spawn('../A')\n\
local b = tasks.spawn('../B')\n\
tasks.join({ a, b })\n\
return 'joined'\n```\n\n";

Expand Down
6 changes: 3 additions & 3 deletions crates/harness-internal/runner/tests/it/effect_loop-vfs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ async fn open_gate_after_the_second_write(
}

/// A main section that writes twice while its child's tool call is out.
const WRITES_BESIDE_A_PARKED_CALL: &str = "local t = tasks.spawn('## Child')\n\
const WRITES_BESIDE_A_PARKED_CALL: &str = "local t = tasks.spawn('../Child')\n\
store.write('first.md', '1')\n\
store.write('second.md', '2')\n\
local _task, _ok, result = tasks.join_any({ t })\n\
Expand Down Expand Up @@ -222,8 +222,8 @@ async fn a_backend_that_panics_drops_its_effect_instead_of_unwinding_the_run() {
const WITHDRAWS_A_WRITE: [(&str, &str); 3] = [
(
"Main",
"local slow = tasks.spawn('## Slow')\n\
local fast = tasks.spawn('## Fast')\n\
"local slow = tasks.spawn('../Slow')\n\
local fast = tasks.spawn('../Fast')\n\
local _task, _ok, result = tasks.join_any({ fast })\n\
tasks.cancel(slow)\n\
return result",
Expand Down
4 changes: 2 additions & 2 deletions crates/harness-internal/runner/tests/it/harness-inbox.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ const LOOPS_THEN_RECEIVES_TWICE: &str = "---\nname: loops-receives-twice\ndescri
const STEERS_A_CHILD: &str = "---\nname: steers-child\ndescription: d\nrunvil: 0\n---\n\n\
# SteersChild\n\n\
## Main\n\n```lua\n\
local child = tasks.spawn('## Child')\n\
local child = tasks.spawn('../Child')\n\
tasks.send(child, (tasks.receive()))\n\
local _t, _ok, report = tasks.join_any({ child })\n\
return report\n```\n\n\
Expand All @@ -105,7 +105,7 @@ const STEERS_A_CHILD_THEN_LOOPS: &str = "---\nname: steers-child-loops\ndescript
runvil: 0\n---\n\n\
# SteersChildLoops\n\n\
## Main\n\n```lua\n\
local child = tasks.spawn('## Child')\n\
local child = tasks.spawn('../Child')\n\
tasks.send(child, (tasks.receive()))\n\
messages:user('mine')\n\
models.loop()\n\
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ const CANCELS_A_HOLDER: &str = "---\nname: cancels\ndescription: d\nrunvil: 0\n\
uses:\n - tests/harness\n---\n\n\
# Cancels\n\n\
## Main\n\n```lua\n\
local t = tasks.spawn('## Child')\n\
local t = tasks.spawn('../Child')\n\
models.infer('wait')\n\
tasks.cancel(t)\n\
models.infer('after')\n\
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ const STOPS_TIMED_JOIN: &str = "---\nname: timed\ndescription: d\nrunvil: 0\n\
---\n\n\
# Timed\n\n\
## Main\n\n```lua\n\
local child = tasks.spawn('## Child')\n\
local child = tasks.spawn('../Child')\n\
local ok, err = pcall(tasks.join_any, { child }, { timeout = 30 })\n\
local after = models.infer('after')\n\
tasks.send(child, 'kept')\n\
Expand All @@ -41,7 +41,7 @@ const STOPS_BEFORE_A_STEP: &str = "---\nname: before-step\ndescription: d\nrunvi
---\n\n\
# BeforeStep\n\n\
## Main\n\n```lua\n\
local child = tasks.spawn('## Child')\n\
local child = tasks.spawn('../Child')\n\
store.write('note.md', 'kept')\n\
local _t, _ok, report = tasks.join_any({ child })\n\
return report\n```\n\n\
Expand Down
4 changes: 2 additions & 2 deletions crates/harness-internal/runner/tests/it/harness-stop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@ const STOPS_ALL: &str = "---\nname: stops\ndescription: d\nrunvil: 0\n\
uses:\n - tests/harness\n---\n\n\
# Stops\n\n\
## Main\n\n```lua\n\
local chat = tasks.spawn('## Chat')\n\
local tool = tasks.spawn('## Tool')\n\
local chat = tasks.spawn('../Chat')\n\
local tool = tasks.spawn('../Tool')\n\
local ok, err = pcall(tasks.join_any, { chat, tool }, { timeout = 30 })\n\
local results = tasks.join({ chat, tool })\n\
return tostring(ok) .. ':' .. err.kind .. '|' .. results[1].result .. '|' .. results[2].result\n```\n\n\
Expand Down
2 changes: 1 addition & 1 deletion crates/harness-internal/runner/tests/it/performers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ async fn a_timer_effect_is_answered_after_its_duration() {

// The first wait times out at 50ms while the child is still parked on
// its 400ms tool call; the second wait, without a timer, delivers it.
let main = "local t = tasks.spawn('## Child')\n\
let main = "local t = tasks.spawn('../Child')\n\
local first = tasks.join_any({ t }, { timeout = 0.05 })\n\
local _task, ok, result = tasks.join_any({ t })\n\
return tostring(first == nil) .. '|' .. tostring(ok) .. '|' .. result";
Expand Down
2 changes: 1 addition & 1 deletion crates/harness-internal/runner/tests/it/render-calls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ async fn call_dynamic_tool_runs_a_model_only_tool_the_render_put_in_the_dynamic_
const ON_TWO_MODELS: [(&str, &str); 3] = [
(
"Main",
"local on_m = tasks.spawn('## OnM')\nlocal on_n = tasks.spawn('## OnN')\n\
"local on_m = tasks.spawn('../OnM')\nlocal on_n = tasks.spawn('../OnN')\n\
local ended = tasks.join({ on_m, on_n })\n\
return ended[1].result .. '|' .. ended[2].result",
),
Expand Down
2 changes: 1 addition & 1 deletion crates/harness-internal/runner/tests/it/support.rs
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ pub(crate) fn run_with_child_and_cancel(main: &str, child: &str, cancel: &Cancel

/// A main section that parks on a 30-second timer beside its child: the
/// child's own tool call and the timer are two effects out at once.
pub(crate) const TIMED_MAIN: &str = "local t = tasks.spawn('## Child')\n\
pub(crate) const TIMED_MAIN: &str = "local t = tasks.spawn('../Child')\n\
local _first, _ok, result = tasks.join_any({ t }, { timeout = 30 })\n\
return result";

Expand Down
1 change: 0 additions & 1 deletion crates/runvil-engine/public-api.txt
Original file line number Diff line number Diff line change
Expand Up @@ -638,7 +638,6 @@ pub runvil_engine::ErrorCode::InvalidRange = 608
pub runvil_engine::ErrorCode::InvalidValue = 106
pub runvil_engine::ErrorCode::Io = 618
pub runvil_engine::ErrorCode::IsADirectory = 604
pub runvil_engine::ErrorCode::ListEmptyItem = 104
pub runvil_engine::ErrorCode::LuaCompile = 202
pub runvil_engine::ErrorCode::LuaQuota = 203
pub runvil_engine::ErrorCode::LuaRuntime = 201
Expand Down

This file was deleted.

6 changes: 3 additions & 3 deletions crates/runvil-engine/tests/suite/effect.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@ use serde_json::{Value, json};
use super::support::{Scenario, TestResult, prompt, shout, text};

const GREET: &str = "
local ask = tasks.spawn('## Ask')
local shout = tasks.spawn('## Shout')
local ask = tasks.spawn('../Ask')
local shout = tasks.spawn('../Shout')
store.write('note.md', 'hello')
local results = tasks.join({ ask, shout })
tasks.join_any({ tasks.spawn('## Quick') }, { timeout = 0.01 })
tasks.join_any({ tasks.spawn('../Quick') }, { timeout = 0.01 })
return results[1].result .. ' / ' .. results[2].result
";

Expand Down
4 changes: 2 additions & 2 deletions crates/runvil-engine/tests/suite/ids.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ use runvil_engine::ids::{Provenance, TaskId};
use super::support::{Scenario, TestResult, prompt, text};

const GREET: &str = "
local first = tasks.spawn('## Reply', { input = 'hello' })
local second = tasks.spawn('## Reply', { input = 'bye' })
local first = tasks.spawn('../Reply', { input = 'hello' })
local second = tasks.spawn('../Reply', { input = 'bye' })
local results = tasks.join({ first, second })
return results[1].result .. ' ' .. results[2].result
";
Expand Down
7 changes: 1 addition & 6 deletions crates/runvil-engine/tests/suite/parsing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use runvil_engine::Prompt;

use super::support::TestResult;

const INVALID_FIXTURES: [(&str, ErrorCode, &str); 6] = [
const INVALID_FIXTURES: [(&str, ErrorCode, &str); 5] = [
("missing-h1", ErrorCode::StructureInvalid, "requires an H1"),
(
"unrecognized-lua-label",
Expand All @@ -31,11 +31,6 @@ const INVALID_FIXTURES: [(&str, ErrorCode, &str); 6] = [
ErrorCode::LuaCompile,
"section `Transform` epilog",
),
(
"list-h3-non-list-content",
ErrorCode::ListEmptyItem,
"empty bullet item",
),
];

#[test]
Expand Down
1 change: 1 addition & 0 deletions crates/runvil-internal/lua/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ publish = false

[dependencies]
mlua.workspace = true
runvil-markdown.workspace = true
runvil-model-client.workspace = true
runvil-types.workspace = true
runvil-vfs.workspace = true
Expand Down
28 changes: 19 additions & 9 deletions crates/runvil-internal/lua/src/__impl_coro.lua
Original file line number Diff line number Diff line change
Expand Up @@ -137,17 +137,25 @@ local function infer(...)
return run_infer(nil, (...))
end

local function call_section(target, input)
local ok, result = yield({
op = "call",
target = target,
input = input,
var = var_snapshot(),
})
if not ok then fail(result) end
return result
-- `call` for the code it is installed for. `from` is nil for a section's
-- own code and a library's identity for that library's code; the request
-- carries it, and the driver resolves the target's path from there.
local function call_from(from)
return function(target, input)
local ok, result = yield({
op = "call",
target = target,
input = input,
var = var_snapshot(),
from = from,
})
if not ok then fail(result) end
return result
end
end

local call_section = call_from(nil)

-- Runs an adhoc tool's handler inside this block coroutine, so every
-- suspending call the handler makes is an ordinary yield of the chain.
-- `jump` refuses while the handler runs; the raw pcall catches every
Expand Down Expand Up @@ -335,6 +343,8 @@ tools.call = tools_call

return {
call = call_section,
-- The factory `require` makes a library's own `call` with.
call_from = call_from,
-- The failure helpers, handed to the `tasks` and `fanout` chunks
-- (`__impl_tasks.lua`, `__impl_fanout.lua`) so their shims raise the one
-- error shape this chunk defines.
Expand Down
18 changes: 15 additions & 3 deletions crates/runvil-internal/lua/src/__impl_fanout.lua
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
-- is an ordinary yield and the scheduler keeps no fanout state of its own.
--
-- The Engine installs this after the coroutine prelude (`__impl_coro.lua`)
-- and installs the returned function as `tasks.fanout`. The chunk
-- and installs the first returned function as `tasks.fanout`. The second
-- makes the `tasks.fanout` of a library. The chunk
-- arguments are privileged captures, never globals: `yield` wraps
-- coroutine.yield, `var_snapshot` is the Engine helper returning the hidden
-- `var` data table as a plain deep copy, `helpers` is the prelude's shared
Expand Down Expand Up @@ -83,7 +84,10 @@ end
-- an arm delivered with a result is canceled right after the `join_any`
-- that delivers it, a failed arm has ended, and a failure cancels the live
-- arms before the function raises.
local function fanout(...)
-- `from` is nil for a section's own code and a library's identity for that
-- library's code; every arm's `spawn` request carries it, so the worker's
-- path resolves from the place the fanout was written in.
local function fanout_impl(from, ...)
local argc = select("#", ...)
local model, worker, collection
if argc == 2 then
Expand Down Expand Up @@ -154,6 +158,7 @@ local function fanout(...)
item = members[index],
index = index,
var = var,
from = from,
})
if not ok then
restore()
Expand Down Expand Up @@ -200,4 +205,11 @@ local function fanout(...)
return results
end

return fanout
-- `tasks.fanout` for the code it is installed for.
local function fanout_from(from)
return function(...)
return fanout_impl(from, ...)
end
end

return fanout_from(nil), fanout_from
Loading
Loading