Skip to main content

Crate dekopon_shell

Crate dekopon_shell 

Source
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 jq filter,
  • 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::parser and 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::lexer tokens to crate::ast.
value
The script value type and its coercion rules.

Structs§

CapabilityDescription
Model-facing metadata for one capability, used by cap --describe.
ExitCode
A script exit code.
Interpreter
A configured script interpreter.
ScriptOutcome
Everything one script execution produced.

Enums§

CapabilityCallResult
The outcome of one capability invocation.

Traits§

CapabilityInvoker
The boundary between this interpreter and the real world.

Functions§

run
Parses and evaluates one script under default bounds.