Metadata-Version: 2.5
Name: jdsl
Version: 0.6.3
Summary: Declarative behavior-tree agents with DSPy-style signatures, run by a tree-walking interpreter over DeepSeek / Anthropic / OpenAI.
Author: Machar Kook
License: Apache-2.0
License-File: LICENSE
Requires-Python: >=3.11
Requires-Dist: anthropic>=0.49
Requires-Dist: mcp>=1.2
Requires-Dist: openai>=1.40
Requires-Dist: python-dotenv>=1.0
Requires-Dist: typer>=0.12
Requires-Dist: typesafe-sdk>=0.7.2
Provides-Extra: harness
Requires-Dist: mcp>=1.2; extra == 'harness'
Provides-Extra: tui
Requires-Dist: textual>=0.50; extra == 'tui'
Description-Content-Type: text/markdown

# jdsl

<p align="center">
  <img src="https://github.com/Cantor-Industries/jdsl-py/blob/e956f7d9140eda507a7914d74949cfb8a22cc1e7/docs/for_respected_human.png" alt="jdsl logo" width="240" />
</p>

> Declarative **behavior-tree agents** as clean Python combinators, run by a
> **tree-walking interpreter** over DeepSeek / Anthropic / OpenAI — with
> **DSPy-style signatures** on the LLM leaves.

Determinism lives in the tree (`seq` / `sel` / `act`); the model enters only at
`predict` leaves, typed by a signature like `"message -> category"`. No codegen,
no object-literal DSL — the tree structure *is* the call structure.

```python
from jdsl import tool, root, seq, sel, act, check, predict, ref, store

@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")                       # provider inferred from the id
    .do(seq(
        predict("message -> category"),           # the model classifies…
        sel(                                      # …and the tree branches on it
            seq(check("category", "billing"), act(route_to_billing)),
            seq(check("category", "support"), act(route_to_support)),
            act(route_to_human),                  # fallback
        ),
    ))
)

ctx = skill.run(message="I was double charged.")
print(ctx.blackboard["category"])                 # -> "billing"
```

…and the model can drive **your** tools. A `react` leaf hands it your `@tool`s and
lets it pick and chain them (native function-calling):

```python
from jdsl import tool, root, react

@tool
def distance_km(origin: str, destination: str) -> int: ...   # your Python
@tool
def drive_hours(km: int) -> float: ...

skill = root("Trip").model("deepseek-chat").do(
    react("request -> answer", tools=[distance_km, drive_hours])
)
skill.run(request="Driving Nairobi to Mombasa — how long?")
```

```
 > distance_km(origin='Nairobi', destination='Mombasa') -> 485
 > drive_hours(km=485)                                  -> 6.1
answer: About 6.1 hours.
```

Triage steers the model with the tree; `react` lets the model steer your tools.
Full version: [examples/trip.py](examples/trip.py).

## Skill authoring TUI

JDSL includes a compact terminal workbench for building restricted behavior
trees without writing the initial Python DSL:

```bash
uv run --extra tui jdsl tui
```

<p align="center">
  <img src="https://github.com/Cantor-Industries/jdsl-py/blob/e956f7d9140eda507a7914d74949cfb8a22cc1e7/docs/tui.png" alt="JDSL skill authoring TUI" width="960" />
</p>

Add sequence, selector, action, guard, predict, react, and repeat nodes; edit
their properties; validate the tree; and save a portable `.jdsl` package. See
the [TUI guide](docs/tui.md) for the authoring workflow.

The workbench uses the Gruvbox Material palette, adapted from
[sainnhe/gruvbox-material](https://github.com/sainnhe/gruvbox-material) and
[curbol/omarchy-gruvbox-material](https://github.com/curbol/omarchy-gruvbox-material).
Truecolor terminals are recommended (`COLORTERM=truecolor`). Use `Ctrl+M` to
toggle reduced motion and `Ctrl+G` to switch between ASCII-safe and Unicode glyphs.

## Install & run

### Quick install (curl)

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

Or with options:
```bash
curl -fsSL https://cantorindustries.com/jdsl-py/install.sh | bash -s -- --version 0.6.0 --no-modify-path
```

Requires Python ≥ 3.11. The curl installer includes the harness dependency as
well as the runtime CLI. After install:
```bash
jdsl about                 # banner + quickstart
jdsl run examples/greeter.py
```

Upgrade a previous curl installation with:

```bash
curl -fsSL https://cantorindustries.com/jdsl-py/install.sh | bash -s -- --uninstall
curl -fsSL https://cantorindustries.com/jdsl-py/install.sh | bash -s -- --version 0.6.0
```

The installed CLI also supports `jdsl uninstall`; captures and provider
credentials are preserved.

### Development (uv)

Requires [uv](https://docs.astral.sh/uv/) and Python ≥ 3.11.

```bash
uv sync

uv run jdsl run examples/greeter.py                       # deterministic, no key
echo 'DEEPSEEK_API_KEY=sk-...' >> .env                    # or ANTHROPIC_API_KEY=...
uv run jdsl run examples/triage.py                        # LLM-driven
uv run pytest                                             # tests (no network)
```

The **provider is inferred from the model id**: `deepseek-chat` → DeepSeek,
`claude-*` → Anthropic, `gpt-*`/`o*` → OpenAI. A `.env` in the working directory
is loaded automatically.

## The API in one screen

| Combinator | Meaning |
| ---------- | ------- |
| `root(name, system=…)` | Entry point. Chain `.model(id)` then `.do(child)`. |
| `seq(*children)` | Run in order; fail fast (behavior-tree AND). |
| `sel(*children)` | Try until one succeeds (behavior-tree OR). |
| `repeat(child, until=…, max=n)` | Loop `child` until `until` succeeds or `max` passes (retry/refine). |
| `invert(child)` · `optional(child)` | Decorators: flip a status · fail-soft (a failing step won't abort its `seq`). |
| `timeout(child, seconds=…)` · `oneshot(child)` | Decorators: bound wall-clock time · run once per run, replay the status. |
| `act(fn, *args)` | Leaf calling a `@tool`; literal args are type-checked. |
| `ref("key")` | An `act` argument resolved from the blackboard at run time. |
| `store(act(…), "key")` | Capture an action's return value onto the blackboard. |
| `check("key", value)` | Guard leaf on `blackboard["key"]`; string matches are case/whitespace-lenient. |
| `predict("a -> b")` | DSPy-style LLM leaf: reads inputs, writes outputs. |
| `react("q -> a", tools=[…])` | Agentic leaf: the model calls `@tool`s in a loop (function-calling) until it answers. |

## Layout

```
jdsl/          the runtime core — dsl, tree (interpreter), context, provider, render, cli
  trace/       canonical trace events, sinks, storage, redaction, replay
  ir/          Behavior IR: schema, safe guard expressions, validation, lowering
  package/     .jdsl manifest, tool contracts, provenance, export + loader
jdsl_harness/  capture + behavior compiler (separate package; optional [harness] extra)
plugins/       Claude Code plugin + Gemini CLI extension (trace capture)
examples/      runnable skills — see examples/README.md
test/          pytest suite (no network; LLM leaves use fakes)
docs/          concepts, API reference, providers, harness & compiler
```

## Behavior harness & compiler

jdsl can also *compile* observed frontier-model behavior into portable policy a
smaller frozen model runs. The harness captures canonical traces, mines and
verifies reusable structure (exact dataflow, sequencing, guards, recovery), and
leaves only irreducible semantic decisions as typed signatures — exported as a
deterministic `.jdsl`.

```bash
jdsl harness serve                 # local capture daemon
jdsl capture inspect <id>          # exact-lineage report
jdsl compile <id> --out retail.jdsl
jdsl package run retail.jdsl --tools tools.py --model <small-model>
```

See [docs/harness_usage.md](docs/harness_usage.md) for the hands-on guide (all
three capture tiers, compile, verify, run) and [docs/harness.md](docs/harness.md)
for the design-to-code map.

## Docs

- [docs/index.md](https://cantor-industries.github.io/jdsl-py/) — MkDocs homepage
- [docs/quickstart.md](docs/quickstart.md) — install and first runs
- [docs/examples.md](docs/examples.md) — guide to the runnable examples
- [docs/concepts.md](docs/concepts.md) — behavior trees, the blackboard, signatures, why no codegen
- [docs/api.md](docs/api.md) — full combinator + node reference
- [docs/providers.md](docs/providers.md) — models, keys, `.env`, routing
- [docs/tui.md](docs/tui.md) — skill authoring TUI
- [docs/harness_usage.md](docs/harness_usage.md) — hands-on: capture, compile, verify, run
- [docs/harness.md](docs/harness.md) — capture harness and behavior compiler overview
- [docs/packages.md](docs/packages.md) — `.jdsl` package format and binding
- [CONTRIBUTING.md](CONTRIBUTING.md) — dev setup, tests, adding examples

## Status

Work in progress. The interpreter, combinator API, signatures, and the
Anthropic / OpenAI-compatible (DeepSeek, OpenAI) backends run today. Not yet
built: Google provider, and the DSPy-defining **optimization** step (tuning
prompts against a metric) — the natural next direction now the runtime exists.

## License

O`Saasy · Author: Cantor Industries
