Expand description
A sandboxed, bash-flavored scripting language whose commands dispatch to Dekopon capabilities.
This crate is a pure interpreter library. It has no notion of Wasmtime, provider components, the
broker, HTTP, the filesystem, or the process environment. Everything a script can reach outside
its own value space goes through one seam, CapabilityInvoker, which the embedding binary
implements.
§What this is for
Exposing one model-facing tool schema per provider capability bloats a system prompt and forces a model into many small round trips. A single scripting tool lets a model express a multi-step plan — loops, conditionals, functions, JSON handling — in one tool call. The “commands” in that script are capability invocations, not operating-system processes.
§Safety model
There is no operating-system sandbox here. This is a native tree-walking evaluator, so every
bound is hand-built in limits:
- a step budget covering statements, loop iterations, function calls, arithmetic nodes, and
values pulled from a
jqfilter, - a shell-function recursion depth cap,
- independent output byte and line ceilings with head-and-tail truncation,
- a wall-clock deadline, re-read on every step and around every capability call,
- a capability-invocation ceiling that is deliberately separate from the step budget,
- a cumulative ceiling on the value bytes a script may materialize, which is what bounds memory for a script that is cheap in steps and expensive in bytes.
One bound is not in limits, because it applies before any budget exists: parser caps
grammar nesting depth at a fixed ceiling. Parsing is recursive and runs on the native stack, so
without it a few kilobytes of nested $( $( ... ) ) aborts the host process instead of
returning a ScriptOutcome.
The variable namespace is seeded only from the script’s own assignments. This interpreter never
reads the host process environment — including through jq, whose standard library exports an
env filter that is deliberately not linked.
§Observability
Every command word a script runs emits a shell.command span with a shell.command.started /
shell.command.completed event pair, so a trace reads as the ordered list of commands a script
actually executed rather than as one opaque “a script ran” entry.
This crate depends on tracing and nothing else for that. It knows no exporter, no collector,
and no telemetry protocol; the embedding binary’s own subscriber decides where these go, the
same way curl here links no HTTP client and only assembles a request for one capability. The
dependency does not compromise the synchronous design constraint below — tracing imposes no
async runtime and is routinely used from fully synchronous code — but it does mean spans may
leave the process, so interp::telemetry documents exactly which fields a command may carry:
never an argument value, and never a model-authored command word.
§Example
use dekopon_shell::{CapabilityCallResult, CapabilityInvoker, Interpreter, Limits};
use serde_json::{Value, json};
struct Fixture;
impl CapabilityInvoker for Fixture {
fn granted(&self) -> Vec<String> {
vec!["echo.echo".to_owned()]
}
fn invoke(&self, capability: &str, input: Value) -> CapabilityCallResult {
assert_eq!(capability, "echo.echo");
CapabilityCallResult::Succeeded(input)
}
}
let outcome = Interpreter::new(Limits::default())
.run("echo.echo --message hi | jq -r .message", &Fixture);
assert_eq!(outcome.exit_code.get(), 0);
assert_eq!(outcome.output, "hi");Re-exports§
pub use limits::DEFAULT_ALLOW_CLOCK;pub use limits::DEFAULT_MAX_CAPABILITY_CALLS;pub use limits::DEFAULT_MAX_OUTPUT_BYTES;pub use limits::DEFAULT_MAX_OUTPUT_LINES;pub use limits::DEFAULT_MAX_RECURSION_DEPTH;pub use limits::DEFAULT_MAX_STEPS;pub use limits::DEFAULT_MAX_VALUE_BYTES;pub use limits::DEFAULT_TIMEOUT;pub use limits::Limits;pub use parser::ParseError;
Modules§
- ast
- Abstract syntax produced by
crate::parserand walked by the evaluator. - lexer
- Tokenizer for the sandboxed shell grammar.
- limits
- Hand-built sandbox bounds for the tree-walking evaluator.
- parser
- Hand-written recursive-descent parser:
crate::lexertokens tocrate::ast. - value
- The script value type and its coercion rules.
Structs§
- Capability
Description - Model-facing metadata for one capability, used by
cap --describe. - Exit
Code - A script exit code.
- Interpreter
- A configured script interpreter.
- Script
Outcome - Everything one script execution produced.
Enums§
- Capability
Call Result - The outcome of one capability invocation.
Traits§
- Capability
Invoker - The boundary between this interpreter and the real world.
Functions§
- run
- Parses and evaluates one script under default bounds.