Metadata-Version: 2.4
Name: nail-lang
Version: 0.9.1
Summary: A programming language designed to be written by AI, not humans.
License: MIT License
        
        Copyright (c) 2026 Watari
        
        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:
        
        The above copyright notice and this permission notice shall be included in all
        copies or substantial portions of the Software.
        
        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.
        
Project-URL: Homepage, https://naillang.com
Project-URL: Repository, https://github.com/watari-ai/nail
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: jsonschema>=4.0
Dynamic: license-file

# NAIL

[![CI](https://github.com/watari-ai/nail/actions/workflows/ci.yml/badge.svg)](https://github.com/watari-ai/nail/actions/workflows/ci.yml)
[![PyPI](https://img.shields.io/pypi/v/nail-lang)](https://pypi.org/project/nail-lang/)

![NAIL effect system — data exfiltration blocked before execution](demos/nail_killer_demo.gif)

NAIL is a CLI tool that lets you:

- Define AI tool schemas once in a typed IR
- Statically validate types and effects before runtime
- Generate OpenAI, Anthropic, and Gemini tool schemas from one source

## Install

```bash
# Recommended — works everywhere, no env conflicts
pipx install nail-lang

# Or with pip (inside a virtualenv, or non-Homebrew Python)
pip install nail-lang
```

## Quick Start

```bash
# Define your tools once (NAIL is JSON + an effects field)
cat tools.nail

# Validate against a specific provider
nail fc check tools.nail --provider openai

# Export to any provider
nail fc convert tools.nail --provider anthropic > anthropic_tools.json
nail fc convert tools.nail --provider gemini   > gemini_tools.json

# Import existing tool schemas into NAIL format
nail fc import openai_tools.json --from openai
```

Try it now → [naillang.com](https://naillang.com)

## Inspect & Validate with nail-lens

`nail-lens` is a companion CLI for human-readable inspection and validation of NAIL spec files — useful for reviewing LLM-generated specs before running them.

```bash
# Inspect a spec file (human-readable summary)
nail-lens inspect tools.nail

# Validate spec version and effects (default: L2)
nail-lens validate tools.nail

# Show all effects declared
nail-lens effects tools.nail

# Diff two spec files
nail-lens diff v1.nail v2.nail
```

Installed automatically with `nail-lang` (`pipx install nail-lang`).

## Why NAIL?

Function calling schemas differ across providers:

```
OpenAI:    { "type": "function", "function": { "parameters": {...} } }
Anthropic: { "name": "...", "input_schema": {...} }
Gemini:    { "name": "...", "parameters": {...} }
```

NAIL gives you one source of truth. Define once, export anywhere.

The **effect system** (`"effects": ["NET", "FS"]`) lets you declare what a tool
can do — network access, file I/O, etc. — and catch violations before the agent runs.

```bash
$ nail fc check tools.nail --provider openai
✗ tools.nail  [3 tool(s)]  provider=openai
  ERROR: Tool 'exfil_data': declared as PURE but has side-effect labels: ['NET']
```

### Spec versioning with `meta.spec_version`

Every NAIL spec file can declare its specification version via the `meta.spec_version` field:

```json
{"nail": "0.1.0", "kind": "fn", "id": "add", "meta": {"spec_version": "0.9.0"}, ...}
```

The checker validates this field at L0: unknown formats produce `CheckError(code="UNSUPPORTED_SPEC_VERSION")`. Specs without the field run in `legacy_mode` (warning only — backward-compatible). This allows tooling and alternative implementations to assert which specification guarantees apply.

## How is NAIL different from...?

> These questions come up constantly. Here are direct answers.

### vs. TypeSpec (Microsoft)

| | TypeSpec | NAIL |
|---|---|---|
| Primary goal | Define REST/GraphQL APIs | Define AI tool schemas + enforce effect isolation |
| Syntax | TypeScript-like text | JSON (AI-writable, no parser needed) |
| Effect system | ❌ None | ✅ IO/NET/FS tracked at type level |
| Cross-provider FC | ❌ Not applicable | ✅ OpenAI / Anthropic / Gemini from one source |
| AI-native | ❌ Human-first | ✅ Designed for LLMs to generate/consume |

TypeSpec is excellent for API-first development by human engineers. NAIL is designed for AI agents to *write* and *verify* their own tool definitions — a different problem.

---

### vs. OpenAPI / JSON Schema

| | OpenAPI / JSON Schema | NAIL |
|---|---|---|
| Describes | REST endpoints / data structures | AI function calls (tool use) |
| Effect system | ❌ None | ✅ Effect isolation enforced |
| Verification layers | ❌ Schema validation only | ✅ L0 (schema) → L1 (types) → L2 (effects) → L3 (termination) |
| Multi-provider | ❌ No concept of "providers" | ✅ Native OpenAI / Anthropic / Gemini converters |
| Termination proofs | ❌ Not applicable | ✅ L3 emits a halt certificate |

OpenAPI describes *what a service exposes*. NAIL describes *what an agent can call* — and proves it's safe to call.

---

### vs. Pydantic

| | Pydantic | NAIL |
|---|---|---|
| Language | Python-specific | Language-agnostic (JSON) |
| Effect system | ❌ None | ✅ First-class, enforced at check time |
| Multi-provider FC | ❌ Manual adapters needed | ✅ Built-in converters |
| AI-writable | ❌ Python syntax required | ✅ Pure JSON, no parser |
| Termination proofs | ❌ Not applicable | ✅ L3 checker |

Pydantic validates Python objects at runtime. NAIL validates *AI tool schemas* — across providers, before execution, including effect isolation.

---

### vs. Rego / OPA

| | Rego / OPA | NAIL |
|---|---|---|
| Domain | Policy enforcement (access control) | AI tool definitions + effect typing |
| Effect system | ❌ Policy rules, not typed effects | ✅ IO/NET/FS in function signatures |
| Multi-provider FC | ❌ Not applicable | ✅ Native converters |
| AI-native | ❌ Human-authored policies | ✅ Designed for LLMs |

Rego excels at answering "is this action allowed?" NAIL answers "does this tool *declaration* have the right effect signature?" — a static, pre-execution guarantee.

---

**TL;DR:** NAIL occupies a gap none of these fill: *AI-native, effect-typed, multi-provider function calling with static verification.*

## How It Works

| Layer | What it checks |
|-------|----------------|
| L0    | JSON structure (schema) |
| L1    | Type correctness |
| L2    | Effect declarations |
| L3    | Termination proofs |

[▶ Full documentation](docs/) · [▶ Playground](https://naillang.com)

---

<!-- Full README continues below -->

<details>
<summary>📖 Full documentation — click to expand</summary>

## What is NAIL?

NAIL is a programming language designed for AI agents to write, verify, and exchange — not for humans to read.

**Three things NAIL solves that no other language does:**

1. **Effect-safe tool routing** — Declare `"effects": ["NET"]` on a tool; NAIL enforces it. AI agents can't call a network tool from a pure sandbox. Enforced at check time, not runtime.
2. **Verifiable AI output** — L0/L1/L2/L3 checkers catch type errors, effect violations, and infinite loops *before* execution. AI-generated code that passes NAIL check is correct for all checked properties (types, effects, and termination under declared annotations).
3. **Cross-provider Function Calling** — `nail_lang.fc_standard` converts NAIL function definitions to OpenAI / Anthropic / Gemini schemas. Write once, deploy to any provider. (v0.8.0)

Modern AI systems generate code and call tools at scale. NAIL gives that scale a formal foundation.

## Core Guarantees

| | Guarantee | Example |
|---|---|---|
| **Zero Ambiguity** | The same spec generates identical code every time | RFC 8785-inspired canonical subset: `json.dumps(sort_keys=True, ensure_ascii=False, separators=(',', ':'))` — one representation, always (non-ASCII preserved as-is) |
| **Effect System** | Side effects tracked at the type level | `fn main [] → fn helper [IO]` is a compile-time error, not a lint warning |
| **Verification Layers** | AI-written code passes 3 independent checks before running | L0 (schema) → L1 (types) → L2 (effects) — all enforced, no silent passes |

## Core Design Principles

1. **AI-first, human-second** — Written and maintained by AI. Human developers interact at the specification layer, not the code layer.
2. **Zero ambiguity** — One way to express every construct. No implicit behavior. No undefined behavior. Enforced by an RFC 8785-inspired canonical subset.
3. **Effects as types** — All side effects (IO, network, filesystem) are declared in function signatures and enforced by the type system.
4. **Verification layers (L0–L2)** — Every program passes schema, type, and effect checks before execution. No silent passes.
5. **Formal verification (v0.6+)** — `nail check --level 3` emits a termination certificate. Provably guaranteed to halt.
6. **Self-evolving** — The language specification itself is developed and improved by AI, with humans providing intent and constraints.

## FAQ: Is NAIL just a JSON AST?

Short answer: no — but it's a fair question.

Most languages use an AST as an *internal* representation. NAIL uses JSON as its **only** representation — there is no text syntax that compiles to it.

What makes NAIL different from "JSON-serialized AST":

**1. The verifier layers are the language.**
The JSON schema (L0), type checker (L1), and effect checker (L2) are not tools built *on top of* NAIL — they *are* NAIL. A program passing all three layers is correct for all checked properties (types, effects, and termination under declared annotations).
Layering is intentional: L0 is minimal by design, while L1/L2 enforce semantic correctness. A termination certificate (L3) is issued when all loops are bounded and all recursive calls carry a strictly decreasing `measure` annotation.

| Layer | Responsibility |
|---|---|
| **L0 (Schema)** | Minimum structural validity — accepts correctly shaped JSON programs |
| **L1 (Type Checker)** | Type correctness — catches int/string mismatches and undefined variables |
| **L2 (Effect Checker)** | Effect isolation — IO in pure functions is a compile-time error |

**2. Effects as first-class types.**
Every function declares its side effects (`io`, `net`, `fs`) in its signature. Calling an IO function from a pure context is a compile-time error — not a lint warning, not a runtime panic.

```json
{"nail":"0.2","kind":"module","defs":[
  {"id":"log_it","effects":["IO"],"params":[{"id":"x","type":{"type":"int","bits":64,"overflow":"panic"}}],"returns":{"type":"unit"},"body":[]},
  {"id":"pure_fn","effects":[],"params":[],"returns":{"type":"unit"},"body":[
    {"op":"call","fn":"log_it","args":[{"lit":1}]}
  ]}
]}
```

```
$ nail check above.nail
CheckError: call to 'log_it' requires effects [IO], but 'pure_fn' only has []
```

No runtime needed. The effect contract is violated at check time.

**3. Canonical form.**
There is exactly one valid JSON representation for any given program. No formatting choices, no style variants. An LLM generating the same logic twice will produce token-for-token identical output.

This is enforced by an RFC 8785-inspired canonical subset (sorted keys + compact separators + `ensure_ascii=False`; does not claim full RFC 8785 compliance): `nail canonicalize` normalizes any NAIL program to its canonical form using `json.dumps(sort_keys=True, ensure_ascii=False, separators=(',', ':'))`, preserving non-ASCII characters as-is rather than escaping them. `nail check --strict` rejects non-canonical input. Example files are stored in canonical form.

**4. Designed for LLM *generation*, not LLM *reading*.**
NAIL is not optimized for an LLM to read existing code. It is optimized for an LLM to write new code: zero ambiguity, zero implicit behavior, zero hallucination surface area.

The analogy: SQL is "just text for querying tables," but the relational model and declarative semantics are what make it SQL — not the text format.

## FAQ: Why JSON and not S-expressions (Lisp)?

Modern LLMs have JSON structured output modes built in (OpenAI, Anthropic, Google all provide `response_format: json`). JSON is the de facto interchange format of AI systems in 2026. Using S-expressions would make NAIL "typed Scheme with effects" — a 60-year-old idea without the novelty.

JSON-as-AST is the differentiator. The canonical form guarantee (`nail canonicalize`) is only possible because JSON has well-defined serialization semantics (NAIL uses an RFC 8785-inspired subset: sorted keys + compact separators). S-expressions have no such standard.

## Python SDK

`nail-lang` ships a full Python SDK (`pip install nail-lang`). Type stubs are included for IDE completion and mypy/pyright support.

### Verify & run NAIL programs

```python
from nail_lang import Checker, Runtime, CheckError

spec = {
    "nail": "0.8.0", "kind": "fn", "id": "add",
    "effects": [], "params": [
        {"id": "a", "type": {"type": "int", "bits": 64, "overflow": "panic"}},
        {"id": "b", "type": {"type": "int", "bits": 64, "overflow": "panic"}},
    ],
    "returns": {"type": "int", "bits": 64, "overflow": "panic"},
    "body": [{"op": "return", "val": {"op": "+", "l": {"ref": "a"}, "r": {"ref": "b"}}}],
}

try:
    Checker(spec, level=3).check()   # L0 schema + L1 types + L2 effects + L3 termination
except CheckError as e:
    print(e.to_json())  # machine-parseable error with code, location, message

result = Runtime(spec).run({"a": 3, "b": 4})
print(result)  # → 7
```

### Effect-safe tool routing

NAIL's effect system can be used directly in Python to sandbox AI agent tool lists:

```python
from nail_lang import filter_by_effects

tools = [
    {"type": "function", "function": {"name": "read_file",  "effects": ["FS"]}},
    {"type": "function", "function": {"name": "http_get",   "effects": ["NET"]}},
    {"type": "function", "function": {"name": "exec_script","effects": ["PROC"]}},
    {"type": "function", "function": {"name": "log",        "effects": ["IO"]}},
]

# Restrict agent to read-only: no network, no process execution
safe_tools = filter_by_effects(tools, allowed=["FS", "IO"])
# → [read_file, log]

# Pass to LiteLLM, OpenAI, or any provider
response = litellm.completion(model="gpt-4o", tools=safe_tools, ...)
```

Unannotated tools are excluded by default (production-safe). See [`integrations/litellm.md`](integrations/litellm.md) for the full integration guide.

### SDK public API

| Symbol | Description |
|---|---|
| `Checker(spec, level=2)` | Verify a NAIL program (L0–L3) |
| `Runtime(spec)` | Execute a verified NAIL program |
| `CheckError` | Structured check-time error (`.to_json()` for machine parsing) |
| `filter_by_effects(tools, allowed)` | Restrict tool list to an effect scope |
| `get_tool_effects(tool)` | Introspect declared effects on a tool |
| `annotate_tool_effects(tool, effects)` | Add effects annotation to a tool |
| `validate_effects(effects)` | Validate effect label list |
| `from_mcp(tool)` / `to_mcp(tool)` | MCP ↔ FC-Standard conversion |
| `to_openai_tool` / `to_anthropic_tool` / `to_gemini_tool` | NAIL → provider conversion |
| `convert_tools(tools, to="anthropic")` | Batch provider conversion |
| `parse_type(spec)` | Parse a type descriptor dict into a `NailType` |
| `VALID_EFFECTS` | `frozenset` of recognised effect kinds |

## FC Standard — Cross-Provider Function Calling

NAIL v0.8.0 introduces `nail_lang.fc_standard`: a unified converter between NAIL function definitions and OpenAI / Anthropic / Gemini Function Calling schemas.

```python
from nail_lang.fc_standard import convert_tools, to_openai_tool, to_anthropic_tool, to_gemini_tool

nail_fn = {
    "nail": "0.8",
    "kind": "fn",
    "id": "search_web",
    "effects": ["NET"],
    "params": [{"id": "query", "type": {"type": "string"}}],
    "returns": {"type": "string"},
    "description": "Search the web and return results"
}

openai_tool    = to_openai_tool(nail_fn)    # OpenAI tools format
anthropic_tool = to_anthropic_tool(nail_fn) # Anthropic tools format
gemini_tool    = to_gemini_tool(nail_fn)    # Gemini functionDeclarations format

# Round-trip guaranteed: NAIL → provider → NAIL preserves structure
```

Write once, deploy to any provider. Effect annotations are preserved across conversions.

See [`nail_lang/fc_standard.py`](nail_lang/fc_standard.py) and the [FC Standard section in SPEC.md](SPEC.md).

## Status

🧪 **Experimental — v0.9.0** on PyPI — `pipx install nail-lang`

| Feature | Status |
|---|---|
| Types: int/float/bool/string/option/list/map/unit | ✅ Implemented |
| Effect system (IO/FS/NET/TIME/RAND/MUT) | ✅ Implemented |
| RFC 8785-inspired canonical subset + `nail canonicalize` + `--strict` | ✅ Implemented |
| `kind: fn` + `kind: module` + function calls | ✅ Implemented |
| Mutable variables (`let mut` + `assign`) | ✅ Implemented |
| Bounded loops + if/else | ✅ Implemented |
| Recursion/cycle detection | ✅ Implemented |
| Return-path exhaustiveness check | ✅ Implemented |
| L0 JSON Schema + L1 Type + L2 Effect checks | ✅ Implemented |
| Overflow modes: `wrap` / `sat` / `panic` | ✅ Implemented (v0.3) |
| Result type (`ok`/`err`/`match_result`) | ✅ Implemented (v0.3) |
| Cross-module import + effect propagation | ✅ Implemented (v0.3) |
| **Type aliases** (module-level `types` dict, circular detection) | ✅ Implemented (v0.4) |
| **Fine-grained Effect capabilities** (path/op allow-lists) | ✅ Implemented (v0.4) |
| **Collection type operations** (`list_get/push/len`, `map_get`) | ✅ Implemented (v0.4) |
| `read_file` (FS) / `http_get` (NET) | ✅ Fully implemented (v0.4) |
| Enum / ADT (`enum_make` / `match_enum`) | ✅ Implemented (v0.5) |
| Core StdLib (`abs`/`clamp`/`min2`/`max2`/`str_len`) | ✅ Implemented (v0.5) |
| FC effect annotations (tool sandbox metadata) | ✅ Implemented (v0.5) |
| **L3 Termination Proofs** (`nail check --level 3`) | ✅ Implemented (v0.6) |
| **`nail check --format json`** (machine-parseable output) | ✅ Implemented (v0.7) |
| **Generics** (`type_params` + `{"type": "param", "name": "T"}`) | ✅ Implemented (v0.7) |
| **Python API** (`nail_lang.filter_by_effects`) | ✅ Implemented (v0.7) |
| **import `"from"` file resolution** | ✅ Implemented (v0.7) |
| Structured JSON errors (`to_json()` / error codes) | ✅ Implemented (v0.7) |
| **Generic type aliases** (module-level `type_params`) | ✅ Implemented (v0.7.2) |
| **FC Standard** (`nail_lang.fc_standard`) | ✅ Implemented (v0.8.0) |
| **Provider converters** (NAIL ↔ OpenAI / Anthropic / Gemini) | ✅ Implemented (v0.8.0) |
| **MCP Bridge** (`from_mcp` / `to_mcp` / `infer_effects`) | ✅ Implemented (v0.7) |
| **Type stubs** (`nail_lang/__init__.pyi`) | ✅ Implemented (v0.8.2) |
| **L3.1: Call-site measure verification** (recursive `measure - k` proof) | ✅ Implemented (v0.8.2) |
| `nail demo` exit code propagation | ✅ Fixed (v0.8.2, #82) |
| Traits / Interfaces / Higher-kinded types | 🔮 Future |
| L4: Memory safety (buffer overflow proofs) | 🔮 Future |

## Secondary Effects: Token Efficiency

A byproduct of NAIL's minimal, unambiguous design is reduced token usage. In a Phase 2 validation experiment (2026-02-22), an LLM implemented the same 5 tasks in both Python and NAIL.

| Metric | NAIL | Python |
|---|---|---|
| Spec validation (L0–L2) | **5/5 (100%)** | N/A |
| Test pass rate | 18/21 (86%) | 21/21 (100%) |
| Avg tokens per function | **173** | 571 |
| Type annotations | Always required (compile error) | Optional |

NAIL used ~70% fewer tokens per function — a secondary benefit of the zero-ambiguity design, not its primary goal. All NAIL failures traced to spec gaps (not AI errors).

→ Full results: [`experiments/phase2/ANALYSIS.md`](./experiments/phase2/ANALYSIS.md)

## Structure

```
nail/
├── SPEC.md          — Language specification
├── PHILOSOPHY.md    — Design rationale and background
├── ROADMAP.md       — Development phases
├── CLI.md           — CLI command reference
├── demos/           — Demo scripts (including e2e_agent_demo.py)
├── examples/        — Sample NAIL programs
├── interpreter/     — Python interpreter (Checker + Runtime)
├── playground/      — Local FastAPI playground (server-based)
├── docs/            — GitHub Pages static playground (Pyodide/WASM)
└── AGENTS.md        — AI agent instructions for this repo
```

## Playground

### 🌐 Online

Try NAIL instantly in your browser — no installation required:

**[https://naillang.com](https://naillang.com)**

Powered by [Pyodide](https://pyodide.org) — the Python interpreter compiled to WebAssembly. The NAIL interpreter runs entirely client-side.

### 💻 Local (FastAPI)

```bash
cd playground
python server.py
# → open http://127.0.0.1:7429
```

Features: live JSON editor, 20+ built-in examples, argument passing, dark theme.
See [`playground/README.md`](./playground/README.md) for details.

## Quick Start

**Browser — no install:**
→ [https://naillang.com](https://naillang.com)

**CLI:** `pipx install nail-lang`

**Requirements:** Python 3.10+

**Clone & run:**
```bash
git clone https://github.com/watari-ai/nail.git
cd nail
pip install -r requirements.txt
./nail run examples/hello.nail
```

---

→ **[CLI.md](./CLI.md)** — full command reference (`nail run`, `nail check`, `nail canonicalize`)

---

*NAIL is built by AI, for AI. Humans define the intent. AI builds the machine.*

</details>
