agentixx 0.1.0__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 (66) hide show
  1. agentixx-0.1.0/.claude/settings.local.json +38 -0
  2. agentixx-0.1.0/.claude/skills/agent-integration/SKILL.md +378 -0
  3. agentixx-0.1.0/.github/workflows/docs.yml +68 -0
  4. agentixx-0.1.0/.github/workflows/test.yml +28 -0
  5. agentixx-0.1.0/.gitignore +220 -0
  6. agentixx-0.1.0/ARCHITECTURE.md +232 -0
  7. agentixx-0.1.0/CLAUDE.md +180 -0
  8. agentixx-0.1.0/LICENSE +21 -0
  9. agentixx-0.1.0/PKG-INFO +200 -0
  10. agentixx-0.1.0/README.md +177 -0
  11. agentixx-0.1.0/ROADMAP.md +118 -0
  12. agentixx-0.1.0/agentix/__init__.py +26 -0
  13. agentixx-0.1.0/agentix/cli/__init__.py +70 -0
  14. agentixx-0.1.0/agentix/cli/__main__.py +10 -0
  15. agentixx-0.1.0/agentix/cli/_resolve.py +57 -0
  16. agentixx-0.1.0/agentix/cli/build.py +247 -0
  17. agentixx-0.1.0/agentix/deployment/__init__.py +16 -0
  18. agentixx-0.1.0/agentix/deployment/_plugin.py +202 -0
  19. agentixx-0.1.0/agentix/deployment/base.py +136 -0
  20. agentixx-0.1.0/agentix/runtime/PROTOCOL.md +173 -0
  21. agentixx-0.1.0/agentix/runtime/__init__.py +19 -0
  22. agentixx-0.1.0/agentix/runtime/client/__init__.py +15 -0
  23. agentixx-0.1.0/agentix/runtime/client/client.py +471 -0
  24. agentixx-0.1.0/agentix/runtime/server/__init__.py +18 -0
  25. agentixx-0.1.0/agentix/runtime/server/app.py +110 -0
  26. agentixx-0.1.0/agentix/runtime/server/sio.py +373 -0
  27. agentixx-0.1.0/agentix/runtime/server/worker/__init__.py +5 -0
  28. agentixx-0.1.0/agentix/runtime/server/worker/__main__.py +6 -0
  29. agentixx-0.1.0/agentix/runtime/server/worker/client.py +403 -0
  30. agentixx-0.1.0/agentix/runtime/server/worker/invoker.py +328 -0
  31. agentixx-0.1.0/agentix/runtime/server/worker/process.py +296 -0
  32. agentixx-0.1.0/agentix/runtime/shared/__init__.py +22 -0
  33. agentixx-0.1.0/agentix/runtime/shared/callables.py +46 -0
  34. agentixx-0.1.0/agentix/runtime/shared/codec.py +102 -0
  35. agentixx-0.1.0/agentix/runtime/shared/events.py +35 -0
  36. agentixx-0.1.0/agentix/runtime/shared/frames.py +29 -0
  37. agentixx-0.1.0/agentix/runtime/shared/framing.py +72 -0
  38. agentixx-0.1.0/agentix/runtime/shared/idents.py +11 -0
  39. agentixx-0.1.0/agentix/runtime/shared/models.py +59 -0
  40. agentixx-0.1.0/agentix/runtime/shared/pump.py +47 -0
  41. agentixx-0.1.0/agentix/runtime/shared/rpc.py +164 -0
  42. agentixx-0.1.0/docs/DEPLOY.md +58 -0
  43. agentixx-0.1.0/docs/concepts/bundles.mdx +107 -0
  44. agentixx-0.1.0/docs/concepts/remote-calls.mdx +139 -0
  45. agentixx-0.1.0/docs/deployment.mdx +106 -0
  46. agentixx-0.1.0/docs/development.mdx +113 -0
  47. agentixx-0.1.0/docs/docs.json +59 -0
  48. agentixx-0.1.0/docs/index.mdx +138 -0
  49. agentixx-0.1.0/docs/integrate-agent.mdx +227 -0
  50. agentixx-0.1.0/docs/integrate-dataset.mdx +188 -0
  51. agentixx-0.1.0/docs/quickstart.mdx +156 -0
  52. agentixx-0.1.0/docs/reference/architecture.mdx +147 -0
  53. agentixx-0.1.0/docs/reference/cli.mdx +84 -0
  54. agentixx-0.1.0/pyproject.toml +75 -0
  55. agentixx-0.1.0/tests/__init__.py +0 -0
  56. agentixx-0.1.0/tests/_rpc_helpers.py +26 -0
  57. agentixx-0.1.0/tests/_user_app_target.py +11 -0
  58. agentixx-0.1.0/tests/_worker_target.py +79 -0
  59. agentixx-0.1.0/tests/conftest.py +133 -0
  60. agentixx-0.1.0/tests/test_models.py +45 -0
  61. agentixx-0.1.0/tests/test_plugin_axes.py +35 -0
  62. agentixx-0.1.0/tests/test_plugin_registry.py +160 -0
  63. agentixx-0.1.0/tests/test_remote_importable_module.py +38 -0
  64. agentixx-0.1.0/tests/test_rpc_protocol.py +216 -0
  65. agentixx-0.1.0/tests/test_worker_subprocess.py +112 -0
  66. agentixx-0.1.0/uv.lock +1448 -0
@@ -0,0 +1,38 @@
1
+ {
2
+ "permissions": {
3
+ "allow": [
4
+ "Bash(chmod +x:*)",
5
+ "Bash(docker run:*)",
6
+ "Bash(mkdir -p agents/claude-code runtime scripts demo/task)",
7
+ "Bash(cp blackbox-agents/agents/claude-code/default.nix agents/claude-code/)",
8
+ "Bash(cp blackbox-agents/demo/task/Dockerfile demo/task/)",
9
+ "Bash(cp blackbox-agents/demo/task/instruction.md demo/task/)",
10
+ "Bash(rm -rf blackbox-agents src)",
11
+ "Read(//nix/store/d7zdxbak09kri15bn76fllf2wws1k5gq-hnix-runtime/bin/**)",
12
+ "Bash(/nix/store/d7zdxbak09kri15bn76fllf2wws1k5gq-hnix-runtime/bin/hnix-info /nix/store/i0w2ybaxdlv463dvr81y3a30aryhv6qm-claude-code-runtime-2.1.96)",
13
+ "WebSearch",
14
+ "WebFetch(domain:github.com)",
15
+ "WebFetch(domain:swe-rex.com)",
16
+ "Read(//nix/store/f7xa531ajj7fk9nwcn35ivxy1lfda91f-hnix-runtime-0.1.0/bin/**)",
17
+ "Bash(/nix/store/f7xa531ajj7fk9nwcn35ivxy1lfda91f-hnix-runtime-0.1.0/bin/hnix-server --help)",
18
+ "Bash(uv sync:*)",
19
+ "Bash(mkdir -p /root/.config/nix)",
20
+ "Read(//root/.config/nix/**)",
21
+ "Bash(git add:*)",
22
+ "Bash(git commit:*)",
23
+ "Read(//usr/bin/**)",
24
+ "Bash(.venv/bin/pip list:*)",
25
+ "Bash(.venv/bin/python -c \"import fastapi; import hnix; print\\('ok'\\)\")",
26
+ "Bash(python3 -m venv .venv)",
27
+ "Bash(.venv/bin/pip install:*)",
28
+ "Bash(.venv/bin/python -c \"import fastapi; import hnix; print\\('all imports ok'\\)\")",
29
+ "Bash(.venv/bin/python -c \"from hnix.runtime.client import RuntimeClient; print\\('ok'\\)\")",
30
+ "Bash(.venv/bin/python -c \"from hnix.runtime.client import RuntimeClient; print\\('hnix import ok'\\)\")",
31
+ "Bash(git push:*)",
32
+ "Bash(gh repo:*)",
33
+ "Bash(git remote:*)",
34
+ "WebFetch(domain:www.harborframework.com)",
35
+ "Bash(/nix/store/0s7azxcgbkczrl5hjzvgi1g8qijlnqx2-claude-code-2.1.97/bin/claude:*)"
36
+ ]
37
+ }
38
+ }
@@ -0,0 +1,378 @@
1
+ ---
2
+ name: agent-integration
3
+ description: Use this skill when the user wants to add a new agent closure to Agentix-Agents-Hub, a new dataset/verifier closure to Agentix-Datasets, or port an existing integration from llm-agents.nix (numtide/llm-agents.nix) into Agentix's typed Python closure convention. Triggers include phrases like "add <name> to agents-hub", "port <name> from llm-agents.nix", "wrap the <cli> CLI as an Agentix closure", "integrate <tool> as a closure service", or any mention of building/adapting a new CLI-driven agent or evaluation harness to run inside an Agentix sandbox.
4
+ version: 1.0.0
5
+ ---
6
+
7
+ # Agent Integration
8
+
9
+ Guide for packaging an external CLI-based agent (or a dataset/verifier) into an **Agentix typed Python closure** — a Docker image that ships a Python package the runtime imports in-process and exposes via `RuntimeClient.remote(<stub>, ...)` calls.
10
+
11
+ Upstream reference for Nix binary packaging:
12
+
13
+ - **llm-agents.nix** — how to pin versions and produce a hermetic `bin/<cli>`. Path: `../llm-agents.nix` (relative to the Agentix repo root).
14
+
15
+ CLI invocation logic (argv, env, flag flow) typically comes from the upstream agent's own docs or any existing wrapper you have handy; this skill describes how to land it in the Agentix closure shape.
16
+
17
+ ## When to activate
18
+
19
+ - "add claude-code / aider / opencode / gemini-cli / qwen-code ... to agents-hub"
20
+ - "port <name> from llm-agents.nix" — llm-agents.nix is the source of truth for the derivation.
21
+ - "wrap the <X> CLI as an Agentix closure"
22
+ - "add a new dataset closure (e.g. humaneval, mbpp, livecodebench) to Agentix-Datasets"
23
+ - The user references llm-agents.nix as a reference / starting point for a new closure.
24
+
25
+ Do **not** use this skill for tasks that only touch `Agentix` core (runtime server, deployment, dispatcher). Those are core-substrate edits, not integrations.
26
+
27
+ ## The Agentix closure ABI (non-negotiable)
28
+
29
+ The final image must satisfy:
30
+
31
+ - `VOLUME /nix`
32
+ - `/nix/store/<hash>-*/` — content-addressed Nix deps for everything the closure needs.
33
+ - `/nix/entry/python/agentix_closures/<name>/` — Python package the runtime imports.
34
+ - `__init__.py` — typed stubs (signatures only; body raises `NotImplementedError`).
35
+ - `_impl.py` — real implementations.
36
+ - `_register.py` — `def register() -> Dispatcher` that binds each stub to its impl.
37
+ - `/nix/entry/manifest.json` — `ClosureManifest` with `abi == AGENTIX_CLOSURE_ABI` and `package = "agentix_closures.<name>"`. This file is what marks `/mnt/<dir>` as a closure; without it the runtime ignores the mount.
38
+ - Optional: `/nix/entry/bin/<cli>` — native binaries the impl shells out to.
39
+
40
+ There is no `bin/start`, no UDS, no FastAPI app inside the closure. Caller-side typing flows from `RuntimeClient.remote(<stub>, ...)`, where `<stub>` is a regular Python callable imported from `agentix_closures.<name>`.
41
+
42
+ See `docs/closure-protocol.md` in Agentix for the full protocol.
43
+
44
+ ## Each closure is self-contained
45
+
46
+ Every closure directory is an **independently maintained unit**. This is deliberately the opposite of llm-agents.nix's monorepo-with-blueprint organization.
47
+
48
+ - Own `Dockerfile`. No shared template file referenced across closures.
49
+ - Own `default.nix`. No `import ../<other>/` across closure boundaries, no shared `lib/` or `overlays/` at the repo root.
50
+ - Own `pyproject.toml`. The Python package is pip-installable on its own (`agentix-closure-<name>`).
51
+ - Own version pins. Two closures can run different versions of the same CLI — that's fine.
52
+ - Own lifecycle: build, test, bump, ship without touching siblings.
53
+
54
+ A single `default.nix` per closure is preferred (inline the binary derivation as a `let` binding); only split into a separate `package.nix` when the file gets unwieldy. Reference material in the Agentix core repo (e.g. `tests/closure-docker/Dockerfile`) is meant to be **copy-pasted as a starting point**, not referenced at build time.
55
+
56
+ ## Target closure layout
57
+
58
+ Every migrated closure lives at `Agentix-Agents-Hub/<name>/` (agents) or `Agentix-Datasets/<name>/` (datasets) with this shape:
59
+
60
+ ```
61
+ <name>/
62
+ ├── pyproject.toml # package metadata: name = "agentix-closure-<name>"
63
+ ├── agentix_closures/
64
+ │ └── <name>/
65
+ │ ├── __init__.py # typed stubs
66
+ │ ├── _impl.py # real implementation
67
+ │ └── _register.py # register() -> Dispatcher
68
+ ├── manifest.json # ClosureManifest (abi, package, ...)
69
+ ├── default.nix # binary derivation(s) + buildPythonPackage + symlinkJoin
70
+ └── Dockerfile # closure image: nix-build → /export → final layer
71
+ ```
72
+
73
+ `__init__.py`, `_impl.py`, `_register.py`, `manifest.json`, `default.nix`, `Dockerfile`, `pyproject.toml` are all mandatory.
74
+
75
+ ## Source mapping
76
+
77
+ **The governing principle: locate → extract, don't copy.** Upstream packaging carries a lot of machinery we don't use (Blueprint flakes, overlays, custom lib hooks, update scripts). For each new closure you only need a few facts out of each source file — everything else goes straight in the bin.
78
+
79
+ ### Lift the CLI invocation into `_impl.py`
80
+
81
+ Whatever your reference is (the upstream CLI's README, an existing harness, your own notes), boil it down to three things and forget the rest:
82
+
83
+ - **argv** — the exact subprocess argument list (e.g. `claude -p <prompt> --output-format=stream-json --permission-mode=bypassPermissions --print --`). Drop it literally into `_impl.py`'s `subprocess` call.
84
+ - **fixed env** — variables the agent hardcodes regardless of caller (`DISABLE_AUTOUPDATER=1`, `IS_SANDBOX=1`, `CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1`, …). Set these inside `_impl.run` when building the subprocess env.
85
+ - **caller-provided env** — variables the agent reads at runtime (`ANTHROPIC_API_KEY`, `ANTHROPIC_BASE_URL`, model selection, …). Expose as a typed `env: dict[str, str] | None = None` keyword argument on the stub; `_impl.run` layers it over `os.environ`.
86
+
87
+ Ignore everything else — Pydantic config schemas, trajectory parsing, multi-backend abstractions, `apt-get`/`npm install` setup logic (Nix replaces them).
88
+
89
+ **Skills / memory / MCP registration**: when the agent normally pulls these from host-side paths (`~/.claude/skills`, etc.), expose them as **typed dict parameters on the stub**, not filesystem paths. A closure has no "host" — the orchestrator ships content inline. Pattern:
90
+
91
+ ```python
92
+ def run(
93
+ instruction: str,
94
+ *,
95
+ skills: dict[str, str] | None = None, # {relpath: content}
96
+ memory: dict[str, str] | None = None,
97
+ mcp_servers: list[dict[str, Any]] | None = None,
98
+ ) -> RunResult: ...
99
+ ```
100
+
101
+ `_impl.py` materialises these under `$CLAUDE_CONFIG_DIR` (or the agent's config dir) right before invoking the CLI. Reject keys that are absolute or contain `..`. Skip the whole mechanism if your agent doesn't use any of it.
102
+
103
+ ### From llm-agents.nix — lift the source descriptor
104
+
105
+ Open `llm-agents.nix/packages/<name>/{package.nix,hashes.json}` and lift just these:
106
+
107
+ - **source descriptor** (URL template + platform map, `fetchFromGitHub` args, or npm tarball URL).
108
+ - **version + hash values** (merge `hashes.json` contents into the derivation — no side-car).
109
+ - **wrap env / PATH additions** from `postFixup` / `postInstall` (only those the agent actually needs).
110
+
111
+ Ignore the rest: Blueprint flake auto-discovery, `overlays/default.nix`, custom hooks (`wrapBuddy`, `versionCheckHook`, `versionCheckHomeHook`, `fetchNpmDepsWithPackuments` — drop these or substitute plain nixpkgs equivalents), `__noChroot`, `passthru.category`, `update.py`.
112
+
113
+ Three derivation shapes cover ~95% of cases. Use `finalAttrs` so the version appears once.
114
+
115
+ **Binary blob** (claude-code, gemini-cli, …):
116
+ ```nix
117
+ stdenv.mkDerivation (finalAttrs: {
118
+ pname = "<name>"; version = "2.1.114";
119
+ src = fetchurl {
120
+ url = "https://<cdn>/${finalAttrs.version}/linux-x64/<binary>";
121
+ hash = "sha256-...";
122
+ };
123
+ dontUnpack = true;
124
+ nativeBuildInputs = [ makeWrapper ];
125
+ installPhase = ''install -Dm755 $src $out/bin/<binary>'';
126
+ postFixup = ''wrapProgram $out/bin/<binary> --set DISABLE_AUTOUPDATER 1'';
127
+ })
128
+ ```
129
+
130
+ **npm package** (pi, opencode, …):
131
+ ```nix
132
+ buildNpmPackage (finalAttrs: {
133
+ pname = "<name>"; version = "0.67.68";
134
+ src = fetchurl {
135
+ url = "https://registry.npmjs.org/<pkg>/-/<pkg>-${finalAttrs.version}.tgz";
136
+ hash = "sha256-...";
137
+ };
138
+ npmDepsHash = "sha256-...";
139
+ dontNpmBuild = true;
140
+ })
141
+ ```
142
+
143
+ **Rust from source** (code, agent-browser, …):
144
+ ```nix
145
+ rustPlatform.buildRustPackage (finalAttrs: {
146
+ pname = "<name>"; version = "0.6.93";
147
+ src = fetchFromGitHub {
148
+ owner = "..."; repo = "..."; tag = "v${finalAttrs.version}";
149
+ hash = "sha256-...";
150
+ };
151
+ cargoHash = "sha256-...";
152
+ })
153
+ ```
154
+
155
+ ## Migration playbook
156
+
157
+ ### 1. Locate source files
158
+
159
+ ```
160
+ llm-agents.nix/packages/<name>/{package.nix,hashes.json}
161
+ ```
162
+
163
+ Plus whatever reference describes the CLI's invocation (the agent's README, an existing wrapper, etc.). Read first, then lift 2-3 specific pieces (see "source mapping" above) and stitch them together — don't copy verbatim.
164
+
165
+ ### 2. `__init__.py` — typed stubs
166
+
167
+ Define the request/response dataclasses and the function signatures. Function bodies raise `NotImplementedError` — they're never called on the caller side.
168
+
169
+ ```python
170
+ from __future__ import annotations
171
+ from dataclasses import dataclass
172
+ from typing import Literal
173
+
174
+ @dataclass
175
+ class RunResult:
176
+ exit_code: int
177
+ stdout: str
178
+ stderr: str
179
+ patch: str
180
+
181
+ def run(
182
+ instruction: str,
183
+ *,
184
+ workdir: str = "/testbed",
185
+ timeout: float = 600,
186
+ model: str | None = None,
187
+ env: dict[str, str] | None = None,
188
+ # … other agent-specific knobs
189
+ ) -> RunResult:
190
+ """Doc for callers — what the agent does, what each arg means."""
191
+ raise NotImplementedError("call via RuntimeClient.remote(<name>.run, ...)")
192
+ ```
193
+
194
+ The signature is the contract. mypy / pyright validate the caller; pydantic on the wire validates the over-the-wire payload.
195
+
196
+ ### 3. `_impl.py` — real bodies
197
+
198
+ Same signature, real body. Subprocess the CLI, build env, capture output, return `RunResult`.
199
+
200
+ ```python
201
+ import asyncio, os
202
+ from . import RunResult
203
+
204
+ async def run(instruction: str, *, workdir: str = "/testbed", ...) -> RunResult:
205
+ env = _build_env(...)
206
+ argv = ["<cli>", "--foo", "bar", "-p", instruction]
207
+ proc = await asyncio.create_subprocess_exec(
208
+ *argv, stdout=PIPE, stderr=PIPE, cwd=workdir, env=env,
209
+ )
210
+ out_b, err_b = await asyncio.wait_for(proc.communicate(), timeout=timeout)
211
+ return RunResult(
212
+ exit_code=proc.returncode or 0,
213
+ stdout=out_b.decode(),
214
+ stderr=err_b.decode(),
215
+ patch=await _collect_patch(workdir),
216
+ )
217
+ ```
218
+
219
+ `_impl.py` can be sync or async — the dispatcher awaits awaitable returns.
220
+
221
+ ### 4. `_register.py` — bind stubs to impls
222
+
223
+ ```python
224
+ from agentix.dispatch import Dispatcher
225
+ from . import run
226
+ from ._impl import run as _run_impl
227
+
228
+ def register() -> Dispatcher:
229
+ d = Dispatcher()
230
+ d.bind(run, _run_impl)
231
+ return d
232
+ ```
233
+
234
+ Pure function, no globals. Runtime calls it once on startup.
235
+
236
+ ### 5. `manifest.json`
237
+
238
+ ```json
239
+ {
240
+ "abi": 1,
241
+ "name": "<name>",
242
+ "version": "0.1.0",
243
+ "kind": "agent",
244
+ "package": "agentix_closures.<name>",
245
+ "description": "Short blurb"
246
+ }
247
+ ```
248
+
249
+ ### 6. `pyproject.toml`
250
+
251
+ ```toml
252
+ [build-system]
253
+ requires = ["hatchling"]
254
+ build-backend = "hatchling.build"
255
+
256
+ [project]
257
+ name = "agentix-closure-<name>"
258
+ version = "0.1.0"
259
+ requires-python = ">=3.11"
260
+ dependencies = [] # closures share the runtime's interpreter; keep this empty
261
+
262
+ [tool.hatch.build.targets.wheel]
263
+ packages = ["agentix_closures/<name>"]
264
+ ```
265
+
266
+ ### 7. `default.nix`
267
+
268
+ Composition: binary derivation + `buildPythonPackage` for the Python wheel + `symlinkJoin` to assemble the final tree.
269
+
270
+ ```nix
271
+ { pkgs ? import <nixpkgs> {} }:
272
+ let
273
+ binary = pkgs.stdenv.mkDerivation (finalAttrs: {
274
+ pname = "<name>"; version = "X.Y.Z";
275
+ src = pkgs.fetchurl { url = "..."; hash = "sha256-..."; };
276
+ # ... installPhase, postFixup, etc.
277
+ });
278
+ python = pkgs.python312;
279
+ closurePkg = python.pkgs.buildPythonPackage {
280
+ pname = "agentix-closure-<name>"; version = "0.1.0"; format = "pyproject";
281
+ src = ./.;
282
+ nativeBuildInputs = [ python.pkgs.hatchling ];
283
+ doCheck = false;
284
+ };
285
+ in
286
+ pkgs.symlinkJoin {
287
+ name = "agentix-closure-<name>";
288
+ paths = [
289
+ binary
290
+ (pkgs.runCommand "<name>-python" { } ''
291
+ mkdir -p $out/python
292
+ cp -r ${closurePkg}/lib/python${python.pythonVersion}/site-packages/agentix_closures $out/python/
293
+ cp ${./manifest.json} $out/manifest.json
294
+ '')
295
+ ];
296
+ }
297
+ ```
298
+
299
+ ### 8. `Dockerfile`
300
+
301
+ Copy `Agentix/tests/closure-docker/Dockerfile` into the closure dir as a starting point. **Context = the closure dir itself** (`<name>/`) — closures are self-contained.
302
+
303
+ If the closure needs a flake input (e.g. `numtide/llm-agents.nix` for the `claude` binary), put a local `flake.nix` + `flake.lock` *inside the closure dir* and build with `nix build .#<name>` inside the Dockerfile's builder stage.
304
+
305
+ Labels worth adding: `LABEL org.agentix.closure=1`, `LABEL org.agentix.closure.kind=agent` (or `dataset`), `LABEL org.agentix.closure.name=<name>`.
306
+
307
+ ### 9. No registration step
308
+
309
+ There's no repo-level registry to update. Each closure's identity is its built Docker image ref plus its `manifest.package`. Callers reference the image by tag in `SandboxConfig(closures=[...])` and import the typed stub: `from agentix_closures import <name>`.
310
+
311
+ ### 10. Build and smoke-test
312
+
313
+ ```bash
314
+ docker build -t agentix-hub/<name>:0.1.0 <name>/ # context = the closure dir
315
+
316
+ # In a tiny script:
317
+ import asyncio
318
+ from agentix import RuntimeClient, SandboxConfig
319
+ from agentix.deployment.docker import DockerDeployment
320
+ from agentix_closures import <name>
321
+
322
+ async def check():
323
+ cfg = SandboxConfig(
324
+ image="ubuntu:24.04",
325
+ runtime="agentix/runtime:0.1.0",
326
+ closures=["agentix-hub/<name>:0.1.0"],
327
+ )
328
+ async with DockerDeployment().session(cfg) as sb:
329
+ async with RuntimeClient(sb.runtime_url) as c:
330
+ print(await c.closures())
331
+ result = await c.remote(<name>.run, instruction="...")
332
+ print(result)
333
+ asyncio.run(check())
334
+ ```
335
+
336
+ Verify the typed return shape, exit code, stdout, patch. Then add an end-to-end test against a known task (e.g. a SWE-bench instance) before declaring done.
337
+
338
+ ## Common pitfalls
339
+
340
+ - **Body in the stub** — `__init__.py` functions must `raise NotImplementedError`, not contain real logic. Real bodies belong in `_impl.py`. A stub with a working body will silently run on the caller side instead of being dispatched.
341
+ - **Imported deps in `__init__.py`** — keep stub imports minimal (stdlib + dataclasses + typing). Heavy imports (subprocess wrappers, fastapi, etc.) belong in `_impl.py` so caller-side `from agentix_closures import <name>` stays cheap and dependency-free.
342
+ - **Agent binary not on PATH inside impl** — your derivation's `symlinkJoin` must produce `bin/<cli>` so it lands at `/mnt/<dir>/entry/bin/<cli>`. `nix-build && ls result/bin/` to verify. The impl invokes the CLI by name; the runtime sandbox makes `entry/bin` resolvable via `paths_from`.
343
+ - **Trajectory overload** — resist piping the agent's full step-by-step trajectory through `run()`. Return `{exit_code, stdout, stderr, patch}` and let callers opt in to richer formats via a separate stub method.
344
+ - **System deps at runtime** — don't add `apt-get install git` in the Dockerfile runtime stage. Put `pkgs.git` in the derivation. The task image usually has `git` already.
345
+ - **Closure Python deps** — keep `pyproject.toml`'s `dependencies = []`. Closures share the runtime's interpreter (`pydantic` is already there). Adding a third-party Python dep to a closure adds risk of collision with other closures' deps.
346
+ - **Flake lives in the closure dir** — if the closure needs a flake input, put the `flake.nix` / `flake.lock` in that closure's own directory and build with `nix build .#<name>` inside the Dockerfile.
347
+ - **Version bumps** — llm-agents.nix uses `hashes.json` + `update.py` for upgrades. We inline everything; bumping a version is a manual edit of the `version = ...` and matching hash lines.
348
+
349
+ ## Datasets variation
350
+
351
+ For `Agentix-Datasets/<name>/`, the layout is the same but the stubs export `setup` and `verify` instead of `run`:
352
+
353
+ ```python
354
+ @dataclass
355
+ class SetupResult:
356
+ instruction: str
357
+ workdir: str
358
+ instance_id: str
359
+
360
+ @dataclass
361
+ class VerifyResult:
362
+ passed: bool
363
+ reason: str
364
+
365
+ def setup(instance_id: str) -> SetupResult: ...
366
+ def verify(patch: str, instance_id: str) -> VerifyResult: ...
367
+ ```
368
+
369
+ There's rarely an external binary to vendor — most datasets only need Python + test tooling. `default.nix` depends on `<nixpkgs>` directly.
370
+
371
+ See `Agentix-Datasets/swebench/` for a working reference.
372
+
373
+ ## Worked example pointer
374
+
375
+ - llm-agents.nix source: `llm-agents.nix/packages/claude-code/{package.nix,hashes.json}`
376
+ - Agentix target: `Agentix-Agents-Hub/claude-code/`
377
+
378
+ When migrating a *new* agent, diff your work against claude-code's target — it's the canonical reference for the typed-Python-closure shape.
@@ -0,0 +1,68 @@
1
+ name: docs
2
+
3
+ on:
4
+ push:
5
+ branches: [master]
6
+ paths:
7
+ - "docs/**"
8
+ - ".github/workflows/docs.yml"
9
+ workflow_dispatch:
10
+
11
+ concurrency:
12
+ group: docs-${{ github.ref }}
13
+ cancel-in-progress: true
14
+
15
+ jobs:
16
+ build-and-deploy:
17
+ runs-on: ubuntu-latest
18
+ steps:
19
+ - uses: actions/checkout@v4
20
+
21
+ - uses: actions/setup-node@v4
22
+ with:
23
+ node-version: "20"
24
+
25
+ - name: Install Mintlify CLI
26
+ run: npm install -g mint
27
+
28
+ - name: Validate docs (strict)
29
+ working-directory: docs
30
+ run: mint validate
31
+
32
+ - name: Build static export
33
+ working-directory: docs
34
+ run: |
35
+ mint export
36
+ rm -rf _site
37
+ mkdir _site
38
+ unzip -q export.zip -d _site
39
+ touch _site/.nojekyll
40
+
41
+ - name: Match code-block font size to body text
42
+ # docs.json has no `customCss` and `fonts` has no `code` sub-object
43
+ # (verified against settings-appearance docs). Inject a tiny <style>
44
+ # block into every page's <head> so fenced code renders at the same
45
+ # size as surrounding prose instead of Mintlify's larger default.
46
+ working-directory: docs
47
+ run: |
48
+ python3 - <<'PY'
49
+ import pathlib
50
+ snippet = (
51
+ '<style>pre,pre *{font-size:1em!important;'
52
+ 'line-height:1.6!important}</style>'
53
+ )
54
+ for html in pathlib.Path('_site').rglob('*.html'):
55
+ s = html.read_text()
56
+ if '</head>' in s and snippet not in s:
57
+ html.write_text(s.replace('</head>', snippet + '</head>', 1))
58
+ PY
59
+
60
+ - name: Deploy to Agentiix.github.io
61
+ uses: peaceiris/actions-gh-pages@v4
62
+ with:
63
+ personal_token: ${{ secrets.DOCS_DEPLOY_TOKEN }}
64
+ external_repository: Agentiix/Agentiix.github.io
65
+ publish_branch: main
66
+ publish_dir: docs/_site
67
+ force_orphan: true
68
+ commit_message: "docs: deploy ${{ github.sha }}"
@@ -0,0 +1,28 @@
1
+ name: test
2
+
3
+ on:
4
+ push:
5
+ branches: [master, main]
6
+ pull_request:
7
+
8
+ concurrency:
9
+ group: test-${{ github.ref }}
10
+ cancel-in-progress: true
11
+
12
+ jobs:
13
+ unit:
14
+ name: unit + lint
15
+ runs-on: ubuntu-latest
16
+ steps:
17
+ - uses: actions/checkout@v4
18
+ - uses: actions/setup-python@v5
19
+ with:
20
+ python-version: "3.11"
21
+ cache: pip
22
+ - run: pip install -e '.[dev]'
23
+ - name: lint
24
+ run: ruff check agentix/ tests/
25
+ - name: types
26
+ run: pyright agentix
27
+ - name: unit tests
28
+ run: pytest tests/ -v