jdsl 0.6.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (164) hide show
  1. jdsl-0.6.2/.github/workflows/docs.yml +57 -0
  2. jdsl-0.6.2/.github/workflows/python-publish.yml +55 -0
  3. jdsl-0.6.2/.gitignore +45 -0
  4. jdsl-0.6.2/CONTRIBUTING.md +61 -0
  5. jdsl-0.6.2/LICENSE +8 -0
  6. jdsl-0.6.2/PKG-INFO +231 -0
  7. jdsl-0.6.2/README.md +211 -0
  8. jdsl-0.6.2/docs/api.md +249 -0
  9. jdsl-0.6.2/docs/cli.md +113 -0
  10. jdsl-0.6.2/docs/code/authoring.md +111 -0
  11. jdsl-0.6.2/docs/code/cli-config.md +123 -0
  12. jdsl-0.6.2/docs/code/compiler.md +218 -0
  13. jdsl-0.6.2/docs/code/packages.md +172 -0
  14. jdsl-0.6.2/docs/code/plugins.md +141 -0
  15. jdsl-0.6.2/docs/code/runtime.md +167 -0
  16. jdsl-0.6.2/docs/code/tracing.md +131 -0
  17. jdsl-0.6.2/docs/compiler.md +157 -0
  18. jdsl-0.6.2/docs/concepts.md +123 -0
  19. jdsl-0.6.2/docs/developer/layout.md +87 -0
  20. jdsl-0.6.2/docs/developer/publishing.md +56 -0
  21. jdsl-0.6.2/docs/developer/tracing.md +50 -0
  22. jdsl-0.6.2/docs/examples.md +43 -0
  23. jdsl-0.6.2/docs/for_respected_human.png +0 -0
  24. jdsl-0.6.2/docs/harness.md +153 -0
  25. jdsl-0.6.2/docs/harness_usage.md +219 -0
  26. jdsl-0.6.2/docs/index.md +125 -0
  27. jdsl-0.6.2/docs/opencode.md +148 -0
  28. jdsl-0.6.2/docs/packages.md +69 -0
  29. jdsl-0.6.2/docs/plugins.md +136 -0
  30. jdsl-0.6.2/docs/providers.md +89 -0
  31. jdsl-0.6.2/docs/quickstart.md +108 -0
  32. jdsl-0.6.2/docs/reference/compiler.md +121 -0
  33. jdsl-0.6.2/docs/reference/runtime.md +86 -0
  34. jdsl-0.6.2/docs/reference/trace-package.md +103 -0
  35. jdsl-0.6.2/docs/security.md +67 -0
  36. jdsl-0.6.2/docs/tui-placeholder.svg +48 -0
  37. jdsl-0.6.2/docs/tui.md +159 -0
  38. jdsl-0.6.2/docs/tui.png +0 -0
  39. jdsl-0.6.2/docs/tutorials/branching.md +68 -0
  40. jdsl-0.6.2/docs/tutorials/react-tools.md +68 -0
  41. jdsl-0.6.2/docs/tutorials/refinement.md +61 -0
  42. jdsl-0.6.2/examples/README.md +31 -0
  43. jdsl-0.6.2/examples/author_behavior.py +208 -0
  44. jdsl-0.6.2/examples/db.py +65 -0
  45. jdsl-0.6.2/examples/gate.py +49 -0
  46. jdsl-0.6.2/examples/greeter.py +21 -0
  47. jdsl-0.6.2/examples/harness/retail_mcp_server.py +104 -0
  48. jdsl-0.6.2/examples/harness/retail_tools.py +75 -0
  49. jdsl-0.6.2/examples/pipeline.py +46 -0
  50. jdsl-0.6.2/examples/react.py +45 -0
  51. jdsl-0.6.2/examples/reason.py +28 -0
  52. jdsl-0.6.2/examples/refine.py +45 -0
  53. jdsl-0.6.2/examples/shop.py +84 -0
  54. jdsl-0.6.2/examples/tinker.py +41 -0
  55. jdsl-0.6.2/examples/triage.py +67 -0
  56. jdsl-0.6.2/examples/triage_jev.py +64 -0
  57. jdsl-0.6.2/examples/trip.py +50 -0
  58. jdsl-0.6.2/examples/tutor.py +156 -0
  59. jdsl-0.6.2/examples/tutor_socratic.py +169 -0
  60. jdsl-0.6.2/examples/wiki.py +75 -0
  61. jdsl-0.6.2/install.sh +310 -0
  62. jdsl-0.6.2/jdsl/__init__.py +116 -0
  63. jdsl-0.6.2/jdsl/cli.py +387 -0
  64. jdsl-0.6.2/jdsl/config.py +80 -0
  65. jdsl-0.6.2/jdsl/context.py +159 -0
  66. jdsl-0.6.2/jdsl/dsl.py +174 -0
  67. jdsl-0.6.2/jdsl/ir/__init__.py +63 -0
  68. jdsl-0.6.2/jdsl/ir/expr.py +193 -0
  69. jdsl-0.6.2/jdsl/ir/lower.py +160 -0
  70. jdsl-0.6.2/jdsl/ir/schema.py +293 -0
  71. jdsl-0.6.2/jdsl/ir/validate.py +134 -0
  72. jdsl-0.6.2/jdsl/package/__init__.py +38 -0
  73. jdsl-0.6.2/jdsl/package/authoring.py +244 -0
  74. jdsl-0.6.2/jdsl/package/export.py +129 -0
  75. jdsl-0.6.2/jdsl/package/load.py +155 -0
  76. jdsl-0.6.2/jdsl/package/manifest.py +120 -0
  77. jdsl-0.6.2/jdsl/package/proposal.py +344 -0
  78. jdsl-0.6.2/jdsl/provider.py +214 -0
  79. jdsl-0.6.2/jdsl/render.py +35 -0
  80. jdsl-0.6.2/jdsl/router.py +31 -0
  81. jdsl-0.6.2/jdsl/trace/__init__.py +41 -0
  82. jdsl-0.6.2/jdsl/trace/blobs.py +56 -0
  83. jdsl-0.6.2/jdsl/trace/events.py +180 -0
  84. jdsl-0.6.2/jdsl/trace/jsonl.py +72 -0
  85. jdsl-0.6.2/jdsl/trace/redaction.py +89 -0
  86. jdsl-0.6.2/jdsl/trace/replay.py +117 -0
  87. jdsl-0.6.2/jdsl/trace/sink.py +110 -0
  88. jdsl-0.6.2/jdsl/tree.py +753 -0
  89. jdsl-0.6.2/jdsl/tui/__init__.py +6 -0
  90. jdsl-0.6.2/jdsl/tui/app.py +164 -0
  91. jdsl-0.6.2/jdsl/tui/commands.py +60 -0
  92. jdsl-0.6.2/jdsl/tui/definitions.py +157 -0
  93. jdsl-0.6.2/jdsl/tui/dialogs.py +56 -0
  94. jdsl-0.6.2/jdsl/tui/editors.py +420 -0
  95. jdsl-0.6.2/jdsl/tui/run_inputs.py +113 -0
  96. jdsl-0.6.2/jdsl/tui/run_panel.py +69 -0
  97. jdsl-0.6.2/jdsl/tui/screens.py +189 -0
  98. jdsl-0.6.2/jdsl/tui/settings.py +67 -0
  99. jdsl-0.6.2/jdsl/tui/signature_editor.py +325 -0
  100. jdsl-0.6.2/jdsl/tui/templates.py +387 -0
  101. jdsl-0.6.2/jdsl/tui/theme.py +33 -0
  102. jdsl-0.6.2/jdsl/tui/workbench.py +1457 -0
  103. jdsl-0.6.2/jdsl/tui/workbench.tcss +181 -0
  104. jdsl-0.6.2/jdsl/tui_skill.py +5 -0
  105. jdsl-0.6.2/jdsl_harness/__init__.py +8 -0
  106. jdsl-0.6.2/jdsl_harness/adapters/__init__.py +6 -0
  107. jdsl-0.6.2/jdsl_harness/adapters/claude_code.py +144 -0
  108. jdsl-0.6.2/jdsl_harness/adapters/correlation.py +88 -0
  109. jdsl-0.6.2/jdsl_harness/adapters/gemini_cli.py +70 -0
  110. jdsl-0.6.2/jdsl_harness/adapters/generic_mcp.py +53 -0
  111. jdsl-0.6.2/jdsl_harness/adapters/import_jsonl.py +86 -0
  112. jdsl-0.6.2/jdsl_harness/adapters/opencode.py +110 -0
  113. jdsl-0.6.2/jdsl_harness/capture.py +129 -0
  114. jdsl-0.6.2/jdsl_harness/compiler/__init__.py +48 -0
  115. jdsl-0.6.2/jdsl_harness/compiler/candidates.py +131 -0
  116. jdsl-0.6.2/jdsl_harness/compiler/consolidate.py +141 -0
  117. jdsl-0.6.2/jdsl_harness/compiler/lineage.py +106 -0
  118. jdsl-0.6.2/jdsl_harness/compiler/model.py +175 -0
  119. jdsl-0.6.2/jdsl_harness/compiler/normalize.py +157 -0
  120. jdsl-0.6.2/jdsl_harness/compiler/package.py +125 -0
  121. jdsl-0.6.2/jdsl_harness/compiler/residualize.py +31 -0
  122. jdsl-0.6.2/jdsl_harness/compiler/staticize.py +258 -0
  123. jdsl-0.6.2/jdsl_harness/compiler/verify.py +127 -0
  124. jdsl-0.6.2/jdsl_harness/gateway.py +107 -0
  125. jdsl-0.6.2/jdsl_harness/mcp_proxy.py +277 -0
  126. jdsl-0.6.2/jdsl_harness/metrics.py +125 -0
  127. jdsl-0.6.2/jdsl_harness/server.py +260 -0
  128. jdsl-0.6.2/jdsl_harness/state.py +64 -0
  129. jdsl-0.6.2/jdsl_harness/store.py +222 -0
  130. jdsl-0.6.2/mkdocs.yml +140 -0
  131. jdsl-0.6.2/plugins/README.md +66 -0
  132. jdsl-0.6.2/plugins/jdsl-claude-plugin/.claude-plugin/plugin.json +7 -0
  133. jdsl-0.6.2/plugins/jdsl-claude-plugin/hooks/hooks.json +18 -0
  134. jdsl-0.6.2/plugins/jdsl-claude-plugin/scripts/jdsl_hook_forwarder.py +57 -0
  135. jdsl-0.6.2/plugins/jdsl-gemini-extension/gemini-extension.json +13 -0
  136. jdsl-0.6.2/plugins/jdsl-gemini-extension/scripts/jdsl_gemini_forwarder.py +57 -0
  137. jdsl-0.6.2/plugins/jdsl-opencode-plugin/README.md +41 -0
  138. jdsl-0.6.2/plugins/jdsl-opencode-plugin/fixtures/tool-after.json +17 -0
  139. jdsl-0.6.2/plugins/jdsl-opencode-plugin/fixtures/tool-before.json +13 -0
  140. jdsl-0.6.2/plugins/jdsl-opencode-plugin/jdsl.ts +125 -0
  141. jdsl-0.6.2/pyproject.toml +71 -0
  142. jdsl-0.6.2/scripts/build_linux_x64_bundle.sh +47 -0
  143. jdsl-0.6.2/scripts/jdsl-linux-launcher +16 -0
  144. jdsl-0.6.2/test/conftest.py +53 -0
  145. jdsl-0.6.2/test/test_author_behavior_example.py +225 -0
  146. jdsl-0.6.2/test/test_blackboard.py +63 -0
  147. jdsl-0.6.2/test/test_cli.py +102 -0
  148. jdsl-0.6.2/test/test_compiler.py +286 -0
  149. jdsl-0.6.2/test/test_config.py +61 -0
  150. jdsl-0.6.2/test/test_decorators.py +72 -0
  151. jdsl-0.6.2/test/test_dsl.py +82 -0
  152. jdsl-0.6.2/test/test_dsl_package.py +88 -0
  153. jdsl-0.6.2/test/test_harness.py +282 -0
  154. jdsl-0.6.2/test/test_ir.py +165 -0
  155. jdsl-0.6.2/test/test_package.py +130 -0
  156. jdsl-0.6.2/test/test_provider.py +137 -0
  157. jdsl-0.6.2/test/test_react.py +128 -0
  158. jdsl-0.6.2/test/test_render.py +39 -0
  159. jdsl-0.6.2/test/test_repeat.py +52 -0
  160. jdsl-0.6.2/test/test_server_cli.py +306 -0
  161. jdsl-0.6.2/test/test_trace.py +255 -0
  162. jdsl-0.6.2/test/test_tree.py +186 -0
  163. jdsl-0.6.2/test/test_tui.py +611 -0
  164. jdsl-0.6.2/test/test_tui_templates.py +29 -0
@@ -0,0 +1,57 @@
1
+ name: docs
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - main
7
+ - master
8
+ - harness
9
+ pull_request:
10
+ paths:
11
+ - docs/**
12
+ - mkdocs.yml
13
+ - pyproject.toml
14
+ - .github/workflows/docs.yml
15
+ workflow_dispatch:
16
+
17
+ permissions:
18
+ contents: write
19
+
20
+ concurrency:
21
+ group: docs
22
+ cancel-in-progress: false
23
+
24
+ jobs:
25
+ build-and-deploy:
26
+ runs-on: ubuntu-latest
27
+ steps:
28
+ - uses: actions/checkout@v4
29
+ with:
30
+ fetch-depth: 0
31
+
32
+ - uses: actions/setup-python@v5
33
+ with:
34
+ python-version: "3.x"
35
+
36
+ - name: Cache pip
37
+ uses: actions/cache@v4
38
+ with:
39
+ key: mkdocs-${{ runner.os }}-${{ hashFiles('mkdocs.yml') }}
40
+ path: ~/.cache/pip
41
+ restore-keys: |
42
+ mkdocs-${{ runner.os }}-
43
+
44
+ - name: Install docs dependencies
45
+ run: pip install "mkdocs>=1.6,<2" "mkdocs-material>=9.5,<10" "mkdocstrings[python]>=0.26,<1" "pymdown-extensions<12.2"
46
+ - name: Build docs
47
+ run: mkdocs build --strict
48
+
49
+ - name: Configure Git Credentials
50
+ if: github.event_name != 'pull_request'
51
+ run: |
52
+ git config user.name github-actions[bot]
53
+ git config user.email 41898282+github-actions[bot]@users.noreply.github.com
54
+
55
+ - name: Deploy docs
56
+ if: github.event_name != 'pull_request'
57
+ run: mkdocs gh-deploy --force --strict --no-history
@@ -0,0 +1,55 @@
1
+ # This workflow will upload a Python Package to PyPI when a release is created
2
+ name: Upload Python Package
3
+
4
+ on:
5
+ release:
6
+ types: [published]
7
+ workflow_dispatch:
8
+
9
+ permissions:
10
+ contents: read
11
+
12
+ jobs:
13
+ release-build:
14
+ runs-on: ubuntu-latest
15
+
16
+ steps:
17
+ - uses: actions/checkout@v4
18
+
19
+ - uses: actions/setup-python@v5
20
+ with:
21
+ python-version: "3.11"
22
+
23
+ - name: Build release distributions
24
+ run: |
25
+ python -m pip install --upgrade pip
26
+ python -m pip install build
27
+ python -m build
28
+
29
+ - name: Upload distributions
30
+ uses: actions/upload-artifact@v4
31
+ with:
32
+ name: release-dists
33
+ path: dist/
34
+
35
+ pypi-publish:
36
+ runs-on: ubuntu-latest
37
+ needs:
38
+ - release-build
39
+ permissions:
40
+ id-token: write
41
+
42
+ environment:
43
+ name: pypi
44
+
45
+ steps:
46
+ - name: Retrieve release distributions
47
+ uses: actions/download-artifact@v4
48
+ with:
49
+ name: release-dists
50
+ path: dist/
51
+
52
+ - name: Publish release distributions to PyPI
53
+ uses: pypa/gh-action-pypi-publish@release/v1
54
+ with:
55
+ packages-dir: dist/
jdsl-0.6.2/.gitignore ADDED
@@ -0,0 +1,45 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *.egg-info/
5
+ build/
6
+ uv.lock
7
+
8
+ dist/
9
+
10
+ # Environments & secrets
11
+ .venv/
12
+ .env
13
+ .env.*
14
+
15
+ # Tooling caches
16
+ .pytest_cache/
17
+ .ruff_cache/
18
+ .mypy_cache/
19
+ .coverage
20
+ htmlcov/
21
+
22
+ # OS / editor
23
+ .DS_Store
24
+ .idea/
25
+ .vscode/
26
+ .claude/
27
+ .opencode
28
+
29
+ # Local agent config (auto-launches servers; per-checkout, like .claude/)
30
+ .mcp.json
31
+
32
+ # Compiled behavior packages: build outputs / scratch compile dirs.
33
+ # (Committed example fixtures live under examples/ and are tracked explicitly.)
34
+ *.jdsl
35
+ /pkgs/
36
+ /pkgs.jdslpkg
37
+
38
+ VERSION_RELEASE.md
39
+
40
+ # Docs build output and local-only docs drafts / experiments.
41
+ site/
42
+ docs/*_next_steps.md
43
+ docs/*_experiment.ipynb
44
+ docs/draft/
45
+ jdsl-linux-x64.tar.gz
@@ -0,0 +1,61 @@
1
+ # Contributing
2
+
3
+ ## Setup
4
+
5
+ ```bash
6
+ uv sync # installs jdsl (editable) + dev tools
7
+ uv run pytest # run the suite
8
+ uv run ruff check jdsl jdsl_harness examples test
9
+ ```
10
+
11
+ Python ≥ 3.11, [uv](https://docs.astral.sh/uv/) for everything.
12
+
13
+ ## Style
14
+
15
+ Terse and dense, in the spirit of tinygrad — small files, one concept each, no
16
+ ceremony. Specifics:
17
+
18
+ - **Line length 120**, 4-space indent (ruff enforces both).
19
+ - Absolute imports only (`from jdsl.tree import ...`).
20
+ - Docstrings explain *why*, not *what the code obviously does*.
21
+ - Prefer a plain function to a class; prefer a combinator to a config object.
22
+ - Keep the public surface (`jdsl/__init__.py`) small and obvious.
23
+
24
+ Run `uv run ruff check jdsl jdsl_harness examples test` before opening a PR; CI
25
+ expects it clean.
26
+
27
+ ## Tests are required
28
+
29
+ Every PR that changes behavior needs tests, and the suite must stay **fast and
30
+ offline** — no network, no real API keys.
31
+
32
+ - Tests live in `test/`, one file per module (`test_tree.py`, `test_dsl.py`, …).
33
+ - The LLM is never called for real. Use the `fake_model` fixture (see
34
+ `test/conftest.py`): a `LanguageModel`-shaped stub whose replies you script,
35
+ and which records every call so you can assert on the prompt/system/model.
36
+ - Cover the branch you touched: a happy path *and* a failure path. Behavior-tree
37
+ nodes have two outcomes (`SUCCESS`/`FAILURE`) — test both.
38
+
39
+ ```bash
40
+ uv run pytest -q # all
41
+ uv run pytest test/test_dsl.py -q
42
+ ```
43
+
44
+ ## Adding an example
45
+
46
+ Examples are teaching material and must run as-is.
47
+
48
+ 1. Put a single-file script in `examples/`, named for what it demonstrates.
49
+ 2. Start with a module docstring: what it shows, and the exact `uv run jdsl run
50
+ examples/greeter.py` command (with any `-i key=value` inputs).
51
+ 3. Deterministic examples need no key; LLM examples read one from `.env`.
52
+ 4. Add a one-line entry to `examples/README.md`.
53
+ 5. Keep external-service calls robust (timeouts, a descriptive User-Agent) — an
54
+ example that crashes on a flaky network teaches nothing.
55
+
56
+ ## Scope of a good PR
57
+
58
+ One idea per PR. New node type, new provider, new example, or a bug fix — not
59
+ all four. If you're adding a node, it needs: the node in `jdsl/tree.py`, a
60
+ combinator in `jdsl/dsl.py`, an export in `jdsl/__init__.py`, a row in
61
+ `docs/api.md`, and tests.
jdsl-0.6.2/LICENSE ADDED
@@ -0,0 +1,8 @@
1
+ Copyright © 2026, Cantor Industries.
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4
+
5
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6
+ No licensee or downstream recipient may use the Software (including any modified or derivative versions) to directly compete with the original Licensor by offering it to third parties as a hosted, managed, or Software-as-a-Service (SaaS) product or cloud service where the primary value of the service is the functionality of the Software itself.
7
+
8
+ THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
jdsl-0.6.2/PKG-INFO ADDED
@@ -0,0 +1,231 @@
1
+ Metadata-Version: 2.5
2
+ Name: jdsl
3
+ Version: 0.6.2
4
+ Summary: Declarative behavior-tree agents with DSPy-style signatures, run by a tree-walking interpreter over DeepSeek / Anthropic / OpenAI.
5
+ Author: Machar Kook
6
+ License: Apache-2.0
7
+ License-File: LICENSE
8
+ Requires-Python: >=3.11
9
+ Requires-Dist: anthropic>=0.49
10
+ Requires-Dist: mcp>=1.2
11
+ Requires-Dist: openai>=1.40
12
+ Requires-Dist: python-dotenv>=1.0
13
+ Requires-Dist: typer>=0.12
14
+ Requires-Dist: typesafe-sdk>=0.7.2
15
+ Provides-Extra: harness
16
+ Requires-Dist: mcp>=1.2; extra == 'harness'
17
+ Provides-Extra: tui
18
+ Requires-Dist: textual>=0.50; extra == 'tui'
19
+ Description-Content-Type: text/markdown
20
+
21
+ # jdsl
22
+
23
+ <p align="center">
24
+ <img src="docs/for_respected_human.png" alt="jdsl logo" width="240" />
25
+ </p>
26
+
27
+ > Declarative **behavior-tree agents** as clean Python combinators, run by a
28
+ > **tree-walking interpreter** over DeepSeek / Anthropic / OpenAI — with
29
+ > **DSPy-style signatures** on the LLM leaves.
30
+
31
+ Determinism lives in the tree (`seq` / `sel` / `act`); the model enters only at
32
+ `predict` leaves, typed by a signature like `"message -> category"`. No codegen,
33
+ no object-literal DSL — the tree structure *is* the call structure.
34
+
35
+ ```python
36
+ from jdsl import tool, root, seq, sel, act, check, predict, ref, store
37
+
38
+ @tool
39
+ def route_to_billing(): print("→ billing")
40
+ @tool
41
+ def route_to_support(): print("→ support")
42
+ @tool
43
+ def route_to_human(): print("→ human")
44
+
45
+ skill = (
46
+ root("Triage", system="Classify inbound messages: billing, support, other.")
47
+ .model("deepseek-chat") # provider inferred from the id
48
+ .do(seq(
49
+ predict("message -> category"), # the model classifies…
50
+ sel( # …and the tree branches on it
51
+ seq(check("category", "billing"), act(route_to_billing)),
52
+ seq(check("category", "support"), act(route_to_support)),
53
+ act(route_to_human), # fallback
54
+ ),
55
+ ))
56
+ )
57
+
58
+ ctx = skill.run(message="I was double charged.")
59
+ print(ctx.blackboard["category"]) # -> "billing"
60
+ ```
61
+
62
+ …and the model can drive **your** tools. A `react` leaf hands it your `@tool`s and
63
+ lets it pick and chain them (native function-calling):
64
+
65
+ ```python
66
+ from jdsl import tool, root, react
67
+
68
+ @tool
69
+ def distance_km(origin: str, destination: str) -> int: ... # your Python
70
+ @tool
71
+ def drive_hours(km: int) -> float: ...
72
+
73
+ skill = root("Trip").model("deepseek-chat").do(
74
+ react("request -> answer", tools=[distance_km, drive_hours])
75
+ )
76
+ skill.run(request="Driving Nairobi to Mombasa — how long?")
77
+ ```
78
+
79
+ ```
80
+ > distance_km(origin='Nairobi', destination='Mombasa') -> 485
81
+ > drive_hours(km=485) -> 6.1
82
+ answer: About 6.1 hours.
83
+ ```
84
+
85
+ Triage steers the model with the tree; `react` lets the model steer your tools.
86
+ Full version: [examples/trip.py](examples/trip.py).
87
+
88
+ ## Skill authoring TUI
89
+
90
+ JDSL includes a compact terminal workbench for building restricted behavior
91
+ trees without writing the initial Python DSL:
92
+
93
+ ```bash
94
+ uv run --extra tui jdsl tui
95
+ ```
96
+
97
+ <p align="center">
98
+ <img src="docs/tui.png" alt="JDSL skill authoring TUI" width="960" />
99
+ </p>
100
+
101
+ Add sequence, selector, action, guard, predict, react, and repeat nodes; edit
102
+ their properties; validate the tree; and save a portable `.jdsl` package. See
103
+ the [TUI guide](docs/tui.md) for the authoring workflow.
104
+
105
+ The workbench uses the Gruvbox Material palette, adapted from
106
+ [sainnhe/gruvbox-material](https://github.com/sainnhe/gruvbox-material) and
107
+ [curbol/omarchy-gruvbox-material](https://github.com/curbol/omarchy-gruvbox-material).
108
+ Truecolor terminals are recommended (`COLORTERM=truecolor`). Use `Ctrl+M` to
109
+ toggle reduced motion and `Ctrl+G` to switch between ASCII-safe and Unicode glyphs.
110
+
111
+ ## Install & run
112
+
113
+ ### Quick install (curl)
114
+
115
+ ```bash
116
+ curl -fsSL https://cantorindustries.com/jdsl-py/install.sh | bash
117
+ ```
118
+
119
+ Or with options:
120
+ ```bash
121
+ curl -fsSL https://cantorindustries.com/jdsl-py/install.sh | bash -s -- --version 0.6.0 --no-modify-path
122
+ ```
123
+
124
+ Requires Python ≥ 3.11. The curl installer includes the harness dependency as
125
+ well as the runtime CLI. After install:
126
+ ```bash
127
+ jdsl about # banner + quickstart
128
+ jdsl run examples/greeter.py
129
+ ```
130
+
131
+ Upgrade a previous curl installation with:
132
+
133
+ ```bash
134
+ curl -fsSL https://cantorindustries.com/jdsl-py/install.sh | bash -s -- --uninstall
135
+ curl -fsSL https://cantorindustries.com/jdsl-py/install.sh | bash -s -- --version 0.6.0
136
+ ```
137
+
138
+ The installed CLI also supports `jdsl uninstall`; captures and provider
139
+ credentials are preserved.
140
+
141
+ ### Development (uv)
142
+
143
+ Requires [uv](https://docs.astral.sh/uv/) and Python ≥ 3.11.
144
+
145
+ ```bash
146
+ uv sync
147
+
148
+ uv run jdsl run examples/greeter.py # deterministic, no key
149
+ echo 'DEEPSEEK_API_KEY=sk-...' >> .env # or ANTHROPIC_API_KEY=...
150
+ uv run jdsl run examples/triage.py # LLM-driven
151
+ uv run pytest # tests (no network)
152
+ ```
153
+
154
+ The **provider is inferred from the model id**: `deepseek-chat` → DeepSeek,
155
+ `claude-*` → Anthropic, `gpt-*`/`o*` → OpenAI. A `.env` in the working directory
156
+ is loaded automatically.
157
+
158
+ ## The API in one screen
159
+
160
+ | Combinator | Meaning |
161
+ | ---------- | ------- |
162
+ | `root(name, system=…)` | Entry point. Chain `.model(id)` then `.do(child)`. |
163
+ | `seq(*children)` | Run in order; fail fast (behavior-tree AND). |
164
+ | `sel(*children)` | Try until one succeeds (behavior-tree OR). |
165
+ | `repeat(child, until=…, max=n)` | Loop `child` until `until` succeeds or `max` passes (retry/refine). |
166
+ | `invert(child)` · `optional(child)` | Decorators: flip a status · fail-soft (a failing step won't abort its `seq`). |
167
+ | `timeout(child, seconds=…)` · `oneshot(child)` | Decorators: bound wall-clock time · run once per run, replay the status. |
168
+ | `act(fn, *args)` | Leaf calling a `@tool`; literal args are type-checked. |
169
+ | `ref("key")` | An `act` argument resolved from the blackboard at run time. |
170
+ | `store(act(…), "key")` | Capture an action's return value onto the blackboard. |
171
+ | `check("key", value)` | Guard leaf on `blackboard["key"]`; string matches are case/whitespace-lenient. |
172
+ | `predict("a -> b")` | DSPy-style LLM leaf: reads inputs, writes outputs. |
173
+ | `react("q -> a", tools=[…])` | Agentic leaf: the model calls `@tool`s in a loop (function-calling) until it answers. |
174
+
175
+ ## Layout
176
+
177
+ ```
178
+ jdsl/ the runtime core — dsl, tree (interpreter), context, provider, render, cli
179
+ trace/ canonical trace events, sinks, storage, redaction, replay
180
+ ir/ Behavior IR: schema, safe guard expressions, validation, lowering
181
+ package/ .jdsl manifest, tool contracts, provenance, export + loader
182
+ jdsl_harness/ capture + behavior compiler (separate package; optional [harness] extra)
183
+ plugins/ Claude Code plugin + Gemini CLI extension (trace capture)
184
+ examples/ runnable skills — see examples/README.md
185
+ test/ pytest suite (no network; LLM leaves use fakes)
186
+ docs/ concepts, API reference, providers, harness & compiler
187
+ ```
188
+
189
+ ## Behavior harness & compiler
190
+
191
+ jdsl can also *compile* observed frontier-model behavior into portable policy a
192
+ smaller frozen model runs. The harness captures canonical traces, mines and
193
+ verifies reusable structure (exact dataflow, sequencing, guards, recovery), and
194
+ leaves only irreducible semantic decisions as typed signatures — exported as a
195
+ deterministic `.jdsl`.
196
+
197
+ ```bash
198
+ jdsl harness serve # local capture daemon
199
+ jdsl capture inspect <id> # exact-lineage report
200
+ jdsl compile <id> --out retail.jdsl
201
+ jdsl package run retail.jdsl --tools tools.py --model <small-model>
202
+ ```
203
+
204
+ See [docs/harness_usage.md](docs/harness_usage.md) for the hands-on guide (all
205
+ three capture tiers, compile, verify, run) and [docs/harness.md](docs/harness.md)
206
+ for the design-to-code map.
207
+
208
+ ## Docs
209
+
210
+ - [docs/index.md](https://cantor-industries.github.io/jdsl-py/) — MkDocs homepage
211
+ - [docs/quickstart.md](docs/quickstart.md) — install and first runs
212
+ - [docs/examples.md](docs/examples.md) — guide to the runnable examples
213
+ - [docs/concepts.md](docs/concepts.md) — behavior trees, the blackboard, signatures, why no codegen
214
+ - [docs/api.md](docs/api.md) — full combinator + node reference
215
+ - [docs/providers.md](docs/providers.md) — models, keys, `.env`, routing
216
+ - [docs/tui.md](docs/tui.md) — skill authoring TUI
217
+ - [docs/harness_usage.md](docs/harness_usage.md) — hands-on: capture, compile, verify, run
218
+ - [docs/harness.md](docs/harness.md) — capture harness and behavior compiler overview
219
+ - [docs/packages.md](docs/packages.md) — `.jdsl` package format and binding
220
+ - [CONTRIBUTING.md](CONTRIBUTING.md) — dev setup, tests, adding examples
221
+
222
+ ## Status
223
+
224
+ Work in progress. The interpreter, combinator API, signatures, and the
225
+ Anthropic / OpenAI-compatible (DeepSeek, OpenAI) backends run today. Not yet
226
+ built: Google provider, and the DSPy-defining **optimization** step (tuning
227
+ prompts against a metric) — the natural next direction now the runtime exists.
228
+
229
+ ## License
230
+
231
+ O`Saasy · Author: Cantor Industries
jdsl-0.6.2/README.md ADDED
@@ -0,0 +1,211 @@
1
+ # jdsl
2
+
3
+ <p align="center">
4
+ <img src="docs/for_respected_human.png" alt="jdsl logo" width="240" />
5
+ </p>
6
+
7
+ > Declarative **behavior-tree agents** as clean Python combinators, run by a
8
+ > **tree-walking interpreter** over DeepSeek / Anthropic / OpenAI — with
9
+ > **DSPy-style signatures** on the LLM leaves.
10
+
11
+ Determinism lives in the tree (`seq` / `sel` / `act`); the model enters only at
12
+ `predict` leaves, typed by a signature like `"message -> category"`. No codegen,
13
+ no object-literal DSL — the tree structure *is* the call structure.
14
+
15
+ ```python
16
+ from jdsl import tool, root, seq, sel, act, check, predict, ref, store
17
+
18
+ @tool
19
+ def route_to_billing(): print("→ billing")
20
+ @tool
21
+ def route_to_support(): print("→ support")
22
+ @tool
23
+ def route_to_human(): print("→ human")
24
+
25
+ skill = (
26
+ root("Triage", system="Classify inbound messages: billing, support, other.")
27
+ .model("deepseek-chat") # provider inferred from the id
28
+ .do(seq(
29
+ predict("message -> category"), # the model classifies…
30
+ sel( # …and the tree branches on it
31
+ seq(check("category", "billing"), act(route_to_billing)),
32
+ seq(check("category", "support"), act(route_to_support)),
33
+ act(route_to_human), # fallback
34
+ ),
35
+ ))
36
+ )
37
+
38
+ ctx = skill.run(message="I was double charged.")
39
+ print(ctx.blackboard["category"]) # -> "billing"
40
+ ```
41
+
42
+ …and the model can drive **your** tools. A `react` leaf hands it your `@tool`s and
43
+ lets it pick and chain them (native function-calling):
44
+
45
+ ```python
46
+ from jdsl import tool, root, react
47
+
48
+ @tool
49
+ def distance_km(origin: str, destination: str) -> int: ... # your Python
50
+ @tool
51
+ def drive_hours(km: int) -> float: ...
52
+
53
+ skill = root("Trip").model("deepseek-chat").do(
54
+ react("request -> answer", tools=[distance_km, drive_hours])
55
+ )
56
+ skill.run(request="Driving Nairobi to Mombasa — how long?")
57
+ ```
58
+
59
+ ```
60
+ > distance_km(origin='Nairobi', destination='Mombasa') -> 485
61
+ > drive_hours(km=485) -> 6.1
62
+ answer: About 6.1 hours.
63
+ ```
64
+
65
+ Triage steers the model with the tree; `react` lets the model steer your tools.
66
+ Full version: [examples/trip.py](examples/trip.py).
67
+
68
+ ## Skill authoring TUI
69
+
70
+ JDSL includes a compact terminal workbench for building restricted behavior
71
+ trees without writing the initial Python DSL:
72
+
73
+ ```bash
74
+ uv run --extra tui jdsl tui
75
+ ```
76
+
77
+ <p align="center">
78
+ <img src="docs/tui.png" alt="JDSL skill authoring TUI" width="960" />
79
+ </p>
80
+
81
+ Add sequence, selector, action, guard, predict, react, and repeat nodes; edit
82
+ their properties; validate the tree; and save a portable `.jdsl` package. See
83
+ the [TUI guide](docs/tui.md) for the authoring workflow.
84
+
85
+ The workbench uses the Gruvbox Material palette, adapted from
86
+ [sainnhe/gruvbox-material](https://github.com/sainnhe/gruvbox-material) and
87
+ [curbol/omarchy-gruvbox-material](https://github.com/curbol/omarchy-gruvbox-material).
88
+ Truecolor terminals are recommended (`COLORTERM=truecolor`). Use `Ctrl+M` to
89
+ toggle reduced motion and `Ctrl+G` to switch between ASCII-safe and Unicode glyphs.
90
+
91
+ ## Install & run
92
+
93
+ ### Quick install (curl)
94
+
95
+ ```bash
96
+ curl -fsSL https://cantorindustries.com/jdsl-py/install.sh | bash
97
+ ```
98
+
99
+ Or with options:
100
+ ```bash
101
+ curl -fsSL https://cantorindustries.com/jdsl-py/install.sh | bash -s -- --version 0.6.0 --no-modify-path
102
+ ```
103
+
104
+ Requires Python ≥ 3.11. The curl installer includes the harness dependency as
105
+ well as the runtime CLI. After install:
106
+ ```bash
107
+ jdsl about # banner + quickstart
108
+ jdsl run examples/greeter.py
109
+ ```
110
+
111
+ Upgrade a previous curl installation with:
112
+
113
+ ```bash
114
+ curl -fsSL https://cantorindustries.com/jdsl-py/install.sh | bash -s -- --uninstall
115
+ curl -fsSL https://cantorindustries.com/jdsl-py/install.sh | bash -s -- --version 0.6.0
116
+ ```
117
+
118
+ The installed CLI also supports `jdsl uninstall`; captures and provider
119
+ credentials are preserved.
120
+
121
+ ### Development (uv)
122
+
123
+ Requires [uv](https://docs.astral.sh/uv/) and Python ≥ 3.11.
124
+
125
+ ```bash
126
+ uv sync
127
+
128
+ uv run jdsl run examples/greeter.py # deterministic, no key
129
+ echo 'DEEPSEEK_API_KEY=sk-...' >> .env # or ANTHROPIC_API_KEY=...
130
+ uv run jdsl run examples/triage.py # LLM-driven
131
+ uv run pytest # tests (no network)
132
+ ```
133
+
134
+ The **provider is inferred from the model id**: `deepseek-chat` → DeepSeek,
135
+ `claude-*` → Anthropic, `gpt-*`/`o*` → OpenAI. A `.env` in the working directory
136
+ is loaded automatically.
137
+
138
+ ## The API in one screen
139
+
140
+ | Combinator | Meaning |
141
+ | ---------- | ------- |
142
+ | `root(name, system=…)` | Entry point. Chain `.model(id)` then `.do(child)`. |
143
+ | `seq(*children)` | Run in order; fail fast (behavior-tree AND). |
144
+ | `sel(*children)` | Try until one succeeds (behavior-tree OR). |
145
+ | `repeat(child, until=…, max=n)` | Loop `child` until `until` succeeds or `max` passes (retry/refine). |
146
+ | `invert(child)` · `optional(child)` | Decorators: flip a status · fail-soft (a failing step won't abort its `seq`). |
147
+ | `timeout(child, seconds=…)` · `oneshot(child)` | Decorators: bound wall-clock time · run once per run, replay the status. |
148
+ | `act(fn, *args)` | Leaf calling a `@tool`; literal args are type-checked. |
149
+ | `ref("key")` | An `act` argument resolved from the blackboard at run time. |
150
+ | `store(act(…), "key")` | Capture an action's return value onto the blackboard. |
151
+ | `check("key", value)` | Guard leaf on `blackboard["key"]`; string matches are case/whitespace-lenient. |
152
+ | `predict("a -> b")` | DSPy-style LLM leaf: reads inputs, writes outputs. |
153
+ | `react("q -> a", tools=[…])` | Agentic leaf: the model calls `@tool`s in a loop (function-calling) until it answers. |
154
+
155
+ ## Layout
156
+
157
+ ```
158
+ jdsl/ the runtime core — dsl, tree (interpreter), context, provider, render, cli
159
+ trace/ canonical trace events, sinks, storage, redaction, replay
160
+ ir/ Behavior IR: schema, safe guard expressions, validation, lowering
161
+ package/ .jdsl manifest, tool contracts, provenance, export + loader
162
+ jdsl_harness/ capture + behavior compiler (separate package; optional [harness] extra)
163
+ plugins/ Claude Code plugin + Gemini CLI extension (trace capture)
164
+ examples/ runnable skills — see examples/README.md
165
+ test/ pytest suite (no network; LLM leaves use fakes)
166
+ docs/ concepts, API reference, providers, harness & compiler
167
+ ```
168
+
169
+ ## Behavior harness & compiler
170
+
171
+ jdsl can also *compile* observed frontier-model behavior into portable policy a
172
+ smaller frozen model runs. The harness captures canonical traces, mines and
173
+ verifies reusable structure (exact dataflow, sequencing, guards, recovery), and
174
+ leaves only irreducible semantic decisions as typed signatures — exported as a
175
+ deterministic `.jdsl`.
176
+
177
+ ```bash
178
+ jdsl harness serve # local capture daemon
179
+ jdsl capture inspect <id> # exact-lineage report
180
+ jdsl compile <id> --out retail.jdsl
181
+ jdsl package run retail.jdsl --tools tools.py --model <small-model>
182
+ ```
183
+
184
+ See [docs/harness_usage.md](docs/harness_usage.md) for the hands-on guide (all
185
+ three capture tiers, compile, verify, run) and [docs/harness.md](docs/harness.md)
186
+ for the design-to-code map.
187
+
188
+ ## Docs
189
+
190
+ - [docs/index.md](https://cantor-industries.github.io/jdsl-py/) — MkDocs homepage
191
+ - [docs/quickstart.md](docs/quickstart.md) — install and first runs
192
+ - [docs/examples.md](docs/examples.md) — guide to the runnable examples
193
+ - [docs/concepts.md](docs/concepts.md) — behavior trees, the blackboard, signatures, why no codegen
194
+ - [docs/api.md](docs/api.md) — full combinator + node reference
195
+ - [docs/providers.md](docs/providers.md) — models, keys, `.env`, routing
196
+ - [docs/tui.md](docs/tui.md) — skill authoring TUI
197
+ - [docs/harness_usage.md](docs/harness_usage.md) — hands-on: capture, compile, verify, run
198
+ - [docs/harness.md](docs/harness.md) — capture harness and behavior compiler overview
199
+ - [docs/packages.md](docs/packages.md) — `.jdsl` package format and binding
200
+ - [CONTRIBUTING.md](CONTRIBUTING.md) — dev setup, tests, adding examples
201
+
202
+ ## Status
203
+
204
+ Work in progress. The interpreter, combinator API, signatures, and the
205
+ Anthropic / OpenAI-compatible (DeepSeek, OpenAI) backends run today. Not yet
206
+ built: Google provider, and the DSPy-defining **optimization** step (tuning
207
+ prompts against a metric) — the natural next direction now the runtime exists.
208
+
209
+ ## License
210
+
211
+ O`Saasy · Author: Cantor Industries