Skip to content

jdsl¤

jdsl is a small Python runtime for behavior-tree agents. You author control flow with normal function calls (seq, sel, repeat, act) and put the model only where the task needs judgment (predict or react).

The core rule is simple: deterministic procedure belongs in the tree, values flow through the blackboard, and model calls have explicit signatures.

from jdsl import act, check, predict, root, sel, seq, tool

@tool
def route_to_billing():
    print("billing")

@tool
def route_to_support():
    print("support")

@tool
def route_to_human():
    print("human")

skill = (
    root("Triage", system="Classify inbound messages: billing, support, other.")
    .model("deepseek-chat")
    .do(seq(
        predict("message -> category"),
        sel(
            seq(check("category", "billing"), act(route_to_billing)),
            seq(check("category", "support"), act(route_to_support)),
            act(route_to_human),
        ),
    ))
)

ctx = skill.run(message="I was double charged.")
print(ctx.blackboard["category"])

Start Here¤

  • Quickstart: install, run a deterministic skill, then run an LLM-backed branch.
  • Concepts: behavior trees, the blackboard, signatures, and scoped context.
  • API Reference: every public combinator and runtime object.
  • Code Walkthrough: how the user-facing API maps to jdsl/dsl.py, jdsl/tree.py, traces, IR, and package binding.
  • Examples: which runnable script to start from.
  • Harness Usage: capture, inspect, compile, and run portable .jdsl packages.

What jdsl Is Optimizing For¤

jdsl is built around one practical claim: most useful agent behavior is not improved by asking a model to rediscover procedure on every run. The procedure should be explicit, testable, and replayable. The model should be called only for the parts that are actually judgment.

That split shows up everywhere in the code:

Problem jdsl answer Source
Branching and sequencing Behavior-tree nodes return SUCCESS or FAILURE. jdsl/tree.py
Values between steps A per-run blackboard records values and write provenance. jdsl/context.py
Simple model decisions predict reads named inputs and writes named outputs. jdsl/tree.py
Tool-using model loops react exposes scoped tools through native function calling. jdsl/tree.py
Capturing behavior Runtime/hooks/proxies emit canonical TraceEvents. jdsl/trace/events.py
Compiling behavior Exact refs and fixed actions are recovered before residual model leaves. jdsl_harness/compiler/
Shipping behavior .jdsl packages carry restricted IR, contracts, signatures, and provenance. jdsl/package/

The docs under Code Walkthrough follow those source files directly.

Install¤

Quick install (curl):

curl -fsSL https://cantorindustries.com/jdsl-py/install.sh | bash

Or with options:

curl -fsSL https://cantorindustries.com/jdsl-py/install.sh | bash -s -- --version 0.1.0 --no-modify-path

Requires Python ≥ 3.11. After install:

jdsl run examples/greeter.py

Development setup (uv):

uv sync

LLM-backed examples read provider keys from .env or stored config. See Providers.

Harness and Compiler¤

jdsl can also compile observed behavior into portable policy. The harness captures canonical traces, mines and verifies reusable structure, and leaves semantic residue as typed signatures. The output is a deterministic .jdsl package.

capture -> normalize -> consolidate -> staticize -> verify -> package

See Harness Usage for the hands-on flow and Behavior Packages for package structure and binding.

Current Boundary¤

The runtime, tracing, offline compiler, package export/load, and host capture adapters are implemented. Some design notes are intentionally kept in docs/drafts/ and are ignored by Git because they describe roadmap work or experiments, not the public shipped surface.