{"slug": "a-new-ai-agent-oriented-programming-language", "title": "A New AI Agent Oriented Programming Language", "summary": "Grenat, a new compiled programming language for building AI agent systems, treats prompts, tools, supervised actor agents, budgets and durable workflows as first-class language constructs, with an effect system that turns prompt injection into a compile-time error (E0412). The language's specification is published in SPEC.md alongside an llms.txt reference for LLMs writing Grenat and example programs including support_desk.grn (multi-agent with human approval) and triage.grn (tests with mocks, evals with an LLM judge). Grenat marks model output as untrusted (~T), requiring it to be checked, human-approved or explicitly trusted before reaching the network, a file, a command, an email or a page, and journals each workflow step so runs resume after a crash without billing a model call twice.", "body_md": "Ruby's syntax, Rust's speed, agents as first-class citizens.\n\nGrenat is a compiled programming language for building AI agent systems: typed prompts,\ntools, supervised actor agents, budgets, durable workflows, and an effect system that\nturns prompt injection into a **compile-time error**.\n\n``` php\nprompt summarize(article: String) -> ~Summary using :fast\n  user \"Summarize: #{article}\"\nend\n\nagent Researcher\n  model :smart\n  tools search_web, read_url\n  budget usd: 2.00, time: 10.min\n\n  on Research(topic: String) -> ~Report\n    run \"Investigate #{topic}\"\n  end\nend\n```\n\n- Specification: [`SPEC.md`](https://github.com/itsmedit/grenat/blob/main/SPEC.md)\n- A compact reference for LLMs writing Grenat: [`llms.txt`](https://github.com/itsmedit/grenat/blob/main/llms.txt)\n- Examples: [`basics.grn`](https://github.com/itsmedit/grenat/blob/main/examples/basics.grn) ,[` reviews.grn`](https://github.com/itsmedit/grenat/blob/main/examples/reviews.grn) (native statistics + validated LLM analysis),[`explorer.grn`](https://github.com/itsmedit/grenat/blob/main/examples/explorer.grn) (a real agent),[`support_desk.grn`](https://github.com/itsmedit/grenat/blob/main/examples/support_desk.grn) (multi-agent, human approval),[`triage.grn`](https://github.com/itsmedit/grenat/blob/main/examples/triage.grn) (tests with mocks, evals with an LLM judge),[`macros.grn`](https://github.com/itsmedit/grenat/blob/main/examples/macros.grn) (compile-time code generation),[`usecases/`](https://github.com/itsmedit/grenat/blob/main/examples/usecases) (twelve agent use cases: support, code review, research, data, documents, a weekly digest, operations, a chat with memory, a team of agents, MCP tools, a knowledge base searched by meaning, meeting minutes from a recording)\n\nEvery snippet below passes `grenat check`, `grenat test` and `grenat fmt --check`.\n\nA `prompt` is a function a model implements. Its return type becomes a JSON schema, and the\n`##` comments describe the fields to the model. A model's answer is **untrusted** (`~T`): it\nmust be checked, approved by a human, or explicitly trusted before it reaches the network, a\nfile, a command, an email or a page — otherwise `grenat check` fails (E0412).\n\n```\nstruct Summary\n  title: String           ## 8 words at most\n  bullets: Array(String)  ## 3 to 5 key points\nend\n\n## Summarizes an article.\nprompt summarize(article: String) -> ~Summary using :fast\n  user \"Summarize:\\n#{article}\"\nend\n\ndef headline(article: String) -> String uses llm\n  summarize(article).check { |s| s.bullets.size.between?(3, 5) }?.title\nend\n```\n\nA `tool` is a function a model may call; an `agent` is an actor whose `run` loop calls the\nmodel and its tools until it produces the handler's return type, within a budget. Effects\n(`uses …`) are capabilities checked by the compiler, then again at run time.\n\n``` php\n## Reads a page of the handbook.\ntool read_page(name: String) -> String uses fs.read(\"./handbook\")\n  File.read(\"./handbook/#{name}\")\nend\n\n## Opens a ticket. A human approves it first.\ntool open_ticket(title: String) -> Int uses net(\"tracker.acme.io\"), human, env\n  approve! \"Open “#{title}”?\"\n  token = Credentials.fetch(:tracker, :token)  # a Secret: never printed, never sent to a model\n  Http.post(\n    \"https://tracker.acme.io/tickets\",\n    json: {title:},\n    headers: {\"Authorization\" => \"Bearer #{token}\"},\n  ).status\nend\n\nagent Support\n  model :smart\n  tools read_page, open_ticket\n  budget usd: 0.50, time: 2.min\n  max_turns 12\n  instructions \"Answer from the handbook only. Open a ticket for bugs.\"\n\n  on Ask(question: String) -> ~String\n    run \"Customer question: #{question}\"\n  end\nend\n\ndef answer(question: String) -> ~String uses llm, fs.read(\"./handbook\"), net(\"tracker.acme.io\"), human, env\n  spawn(Support).ask(Ask(question:))\nend\n```\n\nEach `step` of a `workflow` is journaled: after a crash, or a human answering days later, the\nrun resumes where it stopped and no model call is billed twice.\n\n``` php\ndef recent_releases(repo: String) -> Array(String) uses net(\"api.github.com\"), env, time\n  token = Credentials.fetch(:github, :token)\n  res = Http.get(\n    \"https://api.github.com/repos/#{repo}/releases\",\n    headers: {\"Authorization\" => \"Bearer #{token}\"},\n  )\n  week_ago = Time.now - 7.days\n  res.json.trust!.select { |r| Time.parse(r[\"published_at\"]) > week_ago }.map { |r| r[\"tag_name\"] }\nend\n\nworkflow weekly_digest(monday: String) uses llm, net(\"api.github.com\"), net(\"smtp.acme.io\"), env, human, time\n  tags = step(:fetch) { recent_releases(\"rust-lang/rust\") }\n  digest = step(:summarize) { summarize(tags.join(\", \")).trust! }\n  step(:review) { approve! \"Send “#{digest.title}”?\" }\n  step(:email) do\n    Mail.connect(Credentials.fetch(:smtp, :url)).send(\n      from: \"bot@acme.io\",\n      to: [\"team@acme.io\"],\n      subject: digest.title,\n      body: digest.bullets.join(\"\\n\"),\n    )\n  end\nend\n\nevery cron: \"0 8 * * MON\" do  # UTC, run by `grenat serve`\n  weekly_digest(Time.today)\nend\n## Answers a customer, three sentences at most.\nprompt reply(question: String) -> ~String using :fast\n  user question\nend\n\nget \"/chat\" do |req|\n  stream do |out|  # Server-Sent Events, as the model writes\n    reply(req.params[\"q\"]) { |chunk| out << chunk }\n  end\nend\n```\n\n`grenat serve` reads a mailbox — Gmail, Microsoft 365, any IMAP server — and hands each new\nemail to its handler, then marks it seen or moves it; every field of it is untrusted.\n\n```\non_email Credentials.fetch(:support, :imap_url), every: 1.minute, move_to: \"Done\" do |email|\n  answer = reply(email.text).check { |a| a.size < 2000 }?\n  puts \"#{email.attachments.size} attachments, answer ready: #{answer.size} characters\"\nend\nstruct Passage\n  table :passages\n  id: Int?\n  text: String\n  embedding: Vector(1024)\nend\n\nmigration \"001_create_passages\" do |db|\n  db.migrate(\"CREATE TABLE passages (id #{db.primary_key}, text TEXT NOT NULL, embedding #{db.vector(1024)} NOT NULL)\")\nend\n\ndef index(parts: Array(String)) uses llm, db\n  vectors = embed(:docs, parts)  # one request for many texts\n  parts.each_with_index { |text, i| Passage.create(text:, embedding: vectors[i]) }\nend\n\ndef search(question: String) -> Array(Passage) uses llm, db.read\n  Passage.nearest(:embedding, embed(:docs, question), limit: 3)  # pgvector, or brute force on SQLite\nend\nphp\ndef restart(server: SshSession) -> Bool uses ssh(\"api.acme.com\")\n  server.run([\"systemctl\", \"restart\", \"shop\"]).ok?  # an argument vector: no shell injection\nend\n\ndef main uses ssh(\"api.acme.com\"), env\n  server = Ssh.connect(\"deploy@api.acme.com\", key: Credentials.fetch(:deploy, :ssh_key))\n  puts restart(server)\nend\ntest \"only this week's releases\" do\n  freeze_time(\"2026-10-05T08:00:00Z\") do\n    mock_http \"GET https://api.github.com/repos/rust-lang/rust/releases\", json: [\n      {tag_name: \"1.95.0\", published_at: \"2026-10-01T10:00:00Z\"},\n      {tag_name: \"1.94.0\", published_at: \"2026-08-20T10:00:00Z\"},\n    ]\n    assert_equal [\"1.95.0\"], recent_releases(\"rust-lang/rust\")\n    assert_equal \"Bearer test-github-token\", Http.requests.last[\"headers\"][\"Authorization\"]\n  end\nend\n\ntest \"the agent reads the handbook, then answers\" do\n  File.write(\"./handbook/refunds.md\", \"Refunds: within 30 days.\")\n  mock :smart, replies: [\n    call(:read_page, name: \"refunds.md\"),\n    \"Refunds are possible within 30 days.\",\n  ]\n  assert_equal \"Refunds are possible within 30 days.\", answer(\"Can I get a refund?\").trust!\nend\n\ntest \"a model answer out of bounds is refused\" do\n  mock :fast, replies: [{title: \"Rust 2.0\", bullets: [\"only one\"]}]\n  assert_raises CheckError do\n    headline(\"…\")\n  end\nend\n\ntest \"the chat streams its answer\" do\n  mock :fast, replies: [\"Hello, Ada\"]\n  assert_equal \"Hello, Ada\", request(:get, \"/chat?q=hi\")[\"events\"].first[\"data\"]\nend\n\ntest \"the passage about refunds is found\" do\n  mock_embed :docs  # vectors made from the texts' words\n  index([\"A refund is asked for within 30 days.\", \"Invoices export to CSV.\"])\n  assert_equal \"A refund is asked for within 30 days.\", search(\"How do I get a refund?\").first&.text\nend\n```\n\nOther doubles: `mock_shell`, `mock_ssh`, `mock_mcp`, `mock_transcribe`, `mock_env`,\n`mock_mail(raise: \"SMTP down\")`, `mock_credentials`, `cassette` (real calls recorded once,\nthen replayed), `with_human(approve_all | deny_all)`, `deliver_webhook`, `Jobs.perform`,\n`Mail.deliveries`.\n\n```\n# config/models.yml — the first model is the default one\nfast:\n  provider: anthropic          # anthropic, openai, gemini, mistral, xai, openrouter, groq, deepseek, together, ollama\n  name: claude-haiku-4-5\nsmart:\n  provider: openai\n  name: gpt-5\ndocs:\n  provider: voyage             # embeddings: voyage, openai, gemini, mistral, ollama\n  name: voyage-3.5\n  kind: embedding\n  dimensions: 1024\ngrenat credentials edit                    # config/credentials.yml.enc, AES-256-GCM, key in config/master.key\ngrenat credentials edit --env production   # one per environment, chosen by GRENAT_ENV\n```\n\nKeys are read from the credentials (`openai: {api_key: …}`), else from the provider's variable\n(`OPENAI_API_KEY`…). Anthropic agents use prompt caching by default (` cache: true` extends it to\nprompts and conversations).\n\n```\ngrenat new <name>                      a package: grenat.toml, src/, tests/\ngrenat new --app <name>                an application: database, models, routes, src/app.grn, tests/\ngrenat generate agent|workflow|record|tool|eval <name> [field:Type…]\n                                       a part of the application, with its tests (alias: grenat g)\ngrenat check [<file.grn>…]             names, types, effects, taint and secrets\ngrenat run [--log] [--unchecked] [--no-jit] [<file.grn>] [args…]\n                                       check, then run `main`\ngrenat test [<file.grn>…]              the `test` blocks, offline (mocks and cassettes)\ngrenat eval <file.grn> [name]          the `eval` blocks, against the real models, scored\ngrenat serve [--listen host:port]      routes, webhooks, schedules, mailboxes, exposed tools and agents, job workers\ngrenat console [--listen host:port] [--token <token>]\n                                       the operations console: approvals, jobs, journals, costs, evals\ngrenat migrate                         apply the migrations the database has not seen\ngrenat credentials edit|show [--env <environment>]\n                                       the application's encrypted secrets\ngrenat build [--native] [--release] [<file.grn>] [-o <executable>]\n                                       an executable (--native: without the interpreter;\n                                       --release: optimized by LLVM)\ngrenat fmt [--check] <file.grn | dir>… the canonical layout\ngrenat lsp                             the language server\ngrenat update                          the latest commits of git dependencies\ngrenat parse | tokens <file.grn>       the syntax tree, the tokens\ngrenat --version\n\nsetter init                            add a Facetfile to the current package\nsetter new <name>                      create a facet (a library to share)\nsetter add <name> [\"~> 1.2\"]           use a facet from the indexes (or --path <dir>, --git <url> [--tag <tag>])\nsetter install | update | list         install (and build trusted native facets), update, list\nsetter publish                         tag this facet's version for the indexes\n```\n\n| Variable | Effect | \n|---|---|\n| `ANTHROPIC_API_KEY` ,`OPENAI_API_KEY` , … | a provider's key, when the credentials have none | \n| `GRENAT_LOG=1` | log every model, tool, HTTP and SSH call, and what the JIT compiled (same as `--log` ) | \n| `GRENAT_RECORD=1` | record every cassette again, with real calls | \n| `GRENAT_ENV` | the environment ( `development` by default): which credentials | \n| `GRENAT_MASTER_KEY` | the credentials' key, rather than `config/master.key` | \n| `GRENAT_CONSOLE_TOKEN` | the token of `grenat console` (rather than`--token` ) | \n| `GRENAT_JIT=0` | interpret everything (same as `--no-jit` ) | \n| `GRENAT_HOME` | where `grenat build` finds`lib/grenat/libgrenat_{host,standalone}.a` | \n| `CC` | the linker of `grenat build` (default:`cc` ) | \n\n[`llms.txt`](https://github.com/itsmedit/grenat/blob/main/llms.txt) is a reference of about 4,000 tokens written for models: the syntax, the\neffects, taint, agents, workflows, the standard library, the test doubles and the mistakes to\navoid. Every code block in it passes `grenat check` and `grenat test`.\n\nIt was measured: agents were given two real tasks — a support agent (tools, a ticket opened after\nhuman approval, a structured answer) and a weekly release digest (GitHub, a validated summary,\nemail, resumption after a crash without calling the model again) — once in Python with the\nofficial Anthropic SDK, once in Grenat with `llms.txt` as its only documentation, twice each.\nEvery program passes its tests.\n\n| Average of 2 runs | Python | Grenat | \n|---|---|---|\n| Tokens, support agent | 79,700 | **43,000 (−46%)** | \n| Tokens, weekly digest | **38,300** | 45,000 (+18%) | \n| Tokens, both tasks | 118,000 | **88,000 (−25%)** | \n| Lines of program, support / digest | 237 / 207 | **64 / 79** | \n\nThe agent loop, structured answers, approvals and journaled steps are part of the language, so the code a model writes is 2.5 to 3.5 times shorter; where a task is mostly plumbing (HTTP, dates, email), Python's familiarity still pays.\n\nThe latest release, v0.1.2, has phases 0 to 11; phases 12 to 15 are on `main`.\n\n**Phase 15 — email in**: `on_email` reads a mailbox (Gmail, Microsoft 365, any IMAP server, over\nTLS; app passwords or OAuth tokens) under `grenat serve`, each new email — its PDFs and images\nready for a prompt — handled once then marked seen or moved, a failing one retried then flagged,\nevery field untrusted, a crafted email flagged before it is parsed; `deliver_email` hands a handler messages in tests.\n\n**Phase 14 — the gaps LLMs found**: a benchmark of models writing Grenat from `llms.txt` showed\nwhat they reach for. Time (`Time.parse` for ISO 8601, `Time.iso`, `Time.date`, `Time.weekday`,\n`Time.at`, `Time.now - 7.days`, `freeze_time` in tests), Ruby's everyday methods (`s[0, 4]`,\n`s[1..]`, `flatten`, `each_slice`, `reduce(:+)`, `transform_values`, `format`, `then`…), test\ndoubles (`Http.requests` to assert what was sent, `mock_env`, `mock_mail(raise:)`), and email\nheld to the `net` effect by the checker.\n\n**Phase 13 — what agents need in production**: embeddings and search by meaning (`embed`,\n`Vector(n)` fields, `nearest`, pgvector or brute force), prompt caching (Anthropic breakpoints\nplaced by Grenat, cached tokens in the ledger and the console), streaming (blocks receiving the\nanswer as it is written, Server-Sent Events from routes), and audio (`Audio.read`, `transcribe`,\naudio in prompts).\n\n**Phase 12 — libraries in other languages**: native facets (Rust code called as ordinary\nfunctions, behind a versioned ABI), bridge facets (Ruby or Python functions in a sandboxed\nprocess over JSON-RPC — no interpreter embedded), and two official facets: `sheets` (Excel,\nOpenDocument, CSV) and `html` (CSS selectors, links, tables).\n\n**Phase 11 — reaching servers**: `Ssh.connect`, commands as argument vectors, `upload`,\n`download`, SFTP (` list`, `read`, `write`, `rename`…), host keys always verified; proxies for\n`Http` (SOCKS5, SOCKS4, HTTP) and for SSH.\n\n**Phase 10 — configured, not coded**: secrets encrypted per environment as with Rails\n(`grenat credentials edit`), as `Secret` values the language keeps away from models and logs;\nmodels of ten providers in `config/models.yml`, each reached by the right connector with its key\nfound in the credentials — the provider's name is enough.\n\n**Phase 9 — agents operated from a browser**: `grenat console`, open source like the rest —\napprovals waiting for a human, jobs and their workflow journals (retry), what the models cost\nby agent, workflow and day, eval scores over time, failures and refusals, MCP servers. The\nruntime records what it shows in the application's database.\n\n**Phase 8 — applications of agents**, in the language and its toolchain, with no\nframework on top: routes, records and migrations (SQLite and PostgreSQL), jobs, approvals\nthat wait days for a human in the database, tools and agents served to other programs over\nMCP and HTTP (`expose`), and generators — `grenat new --app`, then\n`grenat generate agent|workflow|record|tool|eval`, each part with its tests.\n\n**Phase 7 — agents in production**, measured by ten real use cases (`examples/usecases`):\nan HTTP client, databases, email, a sandboxed `Shell`, MCP servers, PDFs and images,\nconversations with long-term memory, the Batch API, schedules and webhooks\n(`grenat serve`), and facets — libraries installed by `setter` from a `Facetfile`.\n\n**Phase 6 — ecosystem**: programs of several files and packages (`grenat.toml`, path\nand git dependencies, `grenat.lock`), a language server (` grenat lsp`), compile-time\nmacros, and release builds optimized by LLVM (`grenat build --release`).\n\n**Phase 5 — production-ready**: workflows are durable — each `step` is journaled, and an\ninterrupted run resumes where it stopped, without paying twice for a model call. Tests\nnever reach a real model: `mock` gives the model's replies as plain values, `cassette`\nrecords real calls once and replays them. `eval` measures quality on a dataset, with\n`judge` (an LLM as a judge), and fails under a threshold.\n\n**Phase 4 — native code**: functions over numbers, strings, arrays and structs are\ncompiled to machine code by a Cranelift JIT when the program loads — `fib(35)` runs in\n0.05 s, about 1.7× Rust with the same overflow semantics, 200× faster than the\ninterpreter. Objects are reference counted, Perceus style: no garbage collector, no leak,\nin-place updates of uniquely owned values. `grenat build` compiles a program ahead of time\ninto a standalone executable. Tasks are M:N green threads: 100,000 concurrent tasks fit in\n~1 GB on a few OS threads. Agents are\nactors (one message at a time, deadlocks detected, supervision with restarts), and\n`parallel_map` and `race` run truly in parallel. Before running anything, `grenat` checks\nnames, types, effects and taint: an unvalidated model answer that reaches the network is\na **compile-time error**.\n\n**macOS or Linux, with Homebrew:**\n\n```\nbrew install itsmedit/grenat/grenat\n```\n\n**Any Linux with glibc** (Ubuntu 20.04+, Debian 11+, Fedora, RHEL 9, Amazon Linux 2023…) **or macOS, without a package manager** — the install script puts `grenat` and `setter` in `~/.grenat` (in `/usr/local` as root), checks the archive's SHA-256, and adds them to your `PATH`:\n\n```\ncurl -sSL https://github.com/itsmedit/grenat/releases/latest/download/install.sh | sh\n# options: sh -s -- --version v0.1.1 | --prefix DIR | --no-modify-path | --uninstall\n```\n\nA fresh EC2 instance (Amazon Linux 2023), for instance:\n\n```\nsudo dnf install -y gcc                 # the C linker `grenat build` uses (run, test and serve need none)\ncurl -sSL https://github.com/itsmedit/grenat/releases/latest/download/install.sh | sh\nexec $SHELL -l                          # a new shell, with grenat on the PATH\ngrenat new --app hello && cd hello && grenat test\n```\n\n**apt or dnf**, with the packages of a release:\n\n```\ncurl -LO https://github.com/itsmedit/grenat/releases/download/v0.1.2/grenat_0.1.2_amd64.deb\nsudo apt install ./grenat_0.1.2_amd64.deb                  # Ubuntu, Debian (arm64: _arm64.deb)\nsudo dnf install https://github.com/itsmedit/grenat/releases/download/v0.1.2/grenat-0.1.2-1.x86_64.rpm   # Fedora, RHEL, Amazon Linux (aarch64: .aarch64.rpm)\n```\n\n**Docker** — the official image, for amd64 and arm64, with a C linker for `grenat build`:\n\n```\ndocker run --rm -v \"$PWD\":/app ghcr.io/itsmedit/grenat test\ndocker run --rm -v \"$PWD\":/app -p 3000:3000 ghcr.io/itsmedit/grenat serve --listen 0.0.0.0:3000\n# your application's image\nFROM ghcr.io/itsmedit/grenat:0.1.2\nCOPY . /app\nCMD [\"serve\", \"--listen\", \"0.0.0.0:3000\"]\n```\n\n**From the sources** (Rust, and a C linker: Xcode's command line tools on macOS):\n\n```\ncargo install --locked --path crates/grenat_cli && cargo install --locked --path crates/grenat_setter\ncargo build --release -p grenat_host -p grenat_standalone     # the libraries `grenat build` links\nmkdir -p ~/.cargo/lib/grenat && cp target/release/libgrenat_{host,standalone}.a ~/.cargo/lib/grenat/\n```\n\nAlpine (musl) is not supported by the binaries: use a glibc distribution, or the Docker image.\n\n```\ncargo build\ntarget/debug/grenat run examples/basics.grn            # the core language, no LLM\ntarget/debug/grenat run --log examples/fib.grn        # native code: see what the JIT compiled\ntarget/debug/grenat run --log examples/objects.grn    # strings, arrays, structs, natively\ntarget/debug/grenat build examples/objects.grn && ./objects   # a standalone executable (needs `cc`)\ntarget/debug/grenat build --native examples/objects.grn        # without the interpreter: ~0.5 MB\ntarget/debug/grenat build --native --release examples/fib.grn  # optimized by LLVM (needs clang)\n\nexport ANTHROPIC_API_KEY=sk-ant-…            # or, in an application: grenat credentials edit\ntarget/debug/grenat run --log examples/explorer.grn crates/grenat_parser        # a real agent\ntarget/debug/grenat run examples/support_desk.grn examples/tickets.jsonl        # multi-agent + approval\n\ntarget/debug/grenat new --app desk && cd desk # an application: database, models, routes, tests\ngrenat generate agent triage                 # a part and its tests (also workflow, record, tool, eval)\ngrenat generate record doc text:String \"embedding:Vector(1536)\"   # a record searched by meaning\ngrenat migrate && grenat test && grenat serve\ngrenat console                               # its operations console: http://127.0.0.1:4000\n\ntarget/debug/grenat new hello && cd hello    # a package: grenat.toml, Facetfile, src/, tests/\nsetter add http_tools                        # a facet (library) from an index, like a gem\ngrenat run && grenat test                    # in a package, no file to name\n\ntarget/debug/grenat check examples/*.grn     # names, types, effects, taint\ntarget/debug/grenat fmt examples             # canonical layout (--check: only report)\ntarget/debug/grenat test examples/triage.grn # `test` blocks: mocks and cassettes, never a real model\ntarget/debug/grenat eval examples/triage.grn # `eval` blocks: the real model, scored on a dataset\ncargo test                                   # ~950 tests: unit, integration, CLI, HTTP, MCP, SSH, JIT, build\nscripts/test-linux.sh                        # the same suite on Linux, in Docker\n```\n\nTwo facets ship with Grenat, in [`facets/`](https://github.com/itsmedit/grenat/blob/main/facets): native code the application trusts\nexplicitly, built by `setter install`.\n\n```\n# Facetfile\nfacet \"sheets\", path: \"../grenat/facets/sheets\", native: true\nfacet \"html\", path: \"../grenat/facets/html\", native: true\nrequire \"sheets\"\nrequire \"html\"\n\ndef main uses fs.read, fs.write, net(\"acme.io\")\n  orders = Sheets.records(\"orders.xlsx\", sheet: \"2026\").trust!          # .xlsx, .xls, .ods, CSV\n  big = orders.select { |o| o[\"total\"].to_f > 1000.0 }\n  Sheets.write_csv(\"big_orders.csv\", Sheets.table(big, [\"id\", \"customer\", \"total\"]))\n\n  page = Http.get(\"https://acme.io/pricing\").body\n  prices = table_records(page, \"table.prices\")                           # untrusted, as the page\n  puts prices.size\nend\n```\n\nSee [`facets/sheets`](https://github.com/itsmedit/grenat/blob/main/facets/sheets/README.md) and [` facets/html`](https://github.com/itsmedit/grenat/blob/main/facets/html/README.md).\n\nA facet can ship Rust code, as a gem ships C: a crate depending on `grenat_ext`, whose\nexported functions Grenat calls as ordinary ones. The application trusts it explicitly —\nit runs outside Grenat's sandbox — and `setter install` builds it and writes its declarations:\n\n```\n/// Reads a sheet: a line per row, cells separated by commas.\n#[grenat_ext::export(effects = \"fs.read\")]\npub fn read_sheet(path: String) -> Result<Vec<Vec<String>>, String> { … }\n# Facetfile\nfacet \"sheets\", \"~> 0.1\", native: true\n\n# generated: native def read_sheet(path: String) -> ~Array(Array(String)) uses fs.read\ndef main uses fs.read\n  puts read_sheet(\"sales.csv\").trust!.size\nend\n```\n\nTypes and effects are checked like any call's; the result is untrusted unless the function is\n`pure`, and no secret is ever handed to native code (see `SPEC.md`, phase 12).\n\nA facet can also ship Ruby or Python functions — no interpreter is embedded in Grenat: the facet's server is a separate process, speaking JSON-RPC 2.0 on its standard input and output, with a helper library Grenat ships (standard library only):\n\n```\n# bridge/server.rb, started by `[bridge] command = [\"ruby\", \"bridge/server.rb\"]` in grenat.toml\nrequire \"grenat/bridge\"\n\nGrenat::Bridge.export(:slug, params: {title: :string}, returns: :string, pure: true) do |title:|\n  title.downcase.gsub(/[^a-z0-9]+/, \"-\")\nend\n\nGrenat::Bridge.run\npython\n# Facetfile\nfacet \"texts\", \"~> 0.1\", bridge: true\n\n# generated: native def slug(title: String) -> String pure\ndef main\n  puts slug(\"Hello, World\")\nend\n```\n\nIn Python, `@export` on an annotated function, then `run()` (`from grenat_bridge import export, run`).\nThe server runs sandboxed in the facet's directory — a clean environment, no network unless a\nfunction declares `net`; a relative path it is given resolves there, so pass it absolute ones —\none per facet, kept alive, each call within a timeout; types, effects, taint and secrets are\nchecked as for native code (see `SPEC.md`, phase 12).\n\n`grenat lsp` is a language server (diagnostics as you type, formatting, hover, go to\ndefinition, symbols). In Neovim:\n\n```\nvim.filetype.add({ extension = { grn = \"grenat\" } })\nvim.api.nvim_create_autocmd(\"FileType\", { pattern = \"grenat\", callback = function()\n  vim.lsp.start({ name = \"grenat\", cmd = { \"grenat\", \"lsp\" } })\nend })\n```\n\n| Crate | Role | \n|---|---|\n| `grenat_lexer` | tokens, interpolation, heredocs, `##` doc comments | \n| `grenat_ast` | syntax tree | \n| `grenat_parser` | recursive descent + Pratt, diagnostics with error recovery | \n| `grenat_llm` | model providers: the catalog, Anthropic's Messages API and Chat Completions (OpenAI, Gemini, Mistral, Ollama…), streaming, prompt caching, embeddings (those and Voyage), transcriptions (OpenAI's, uploaded as `multipart/form-data` ) and audio in prompts (OpenAI, Gemini); mocks, fake embeddings and transcripts, cassettes and a scripted provider for tests | \n| `grenat_types` | checker: names, types, effects, `~T` taint, secrets (E0100–E0500) | \n| `grenat_codegen` | Cranelift: typing, liveness (Perceus), translation, boundary; JIT and object files; LLVM IR for release builds | \n| `grenat_runtime` | reference-counted strings, arrays and records called by native code | \n| `grenat_driver` | load, check and run a program (shared by the CLI and built executables) | \n| `grenat_host` | static library linked into the executables of `grenat build` | \n| `grenat_standalone` | static library linked into `grenat build --native` executables | \n| `grenat_report` | diagnostic rendering, in the file each error points into | \n| `grenat_db` | databases: SQLite (embedded) and PostgreSQL behind one interface; vectors (pgvector, or bytes searched by brute force) | \n| `grenat_mcp` | the Model Context Protocol: a client (stdio and HTTP), and the server side of `expose` | \n| `grenat_ssh` | SSH and SFTP: host keys verified, commands quoted, SOCKS5 proxies, a blocking API; a real server in process for tests | \n| `grenat_imap` | IMAP over TLS (implicit or STARTTLS): password or OAuth ( `XOAUTH2` ) logins, unseen messages by UID, flags and moves, MIME parsed into what`on_email` gives; a server in process for tests | \n| `grenat_serve` | triggers: cron schedules, calendar arithmetic, webhook signatures, the HTTP server of `grenat serve` , streamed responses | \n| `grenat_generate` | `grenat new --app` and`grenat generate` : an application's parts, with their tests | \n| `grenat_ops` | the operations store: jobs, approvals, model calls, events, eval runs, workflow journals | \n| `grenat_console` | `grenat console` : pages and actions of the operations console, and who may use it | \n| `grenat_config` | the application's configuration: encrypted credentials per environment, `config/*.yml` | \n| `grenat_setter` | `setter` : creates, adds, installs and publishes facets (libraries) | \n| `grenat_package` | `grenat.toml` ,`require` , facets (`Facetfile` , versions, indexes, trusted native code and bridges), path and git dependencies | \n| `grenat_ext` | the SDK of native facets: Rust functions exported to Grenat behind a versioned JSON ABI, and their manifest | \n| `grenat_ext_macros` | `#[grenat_ext::export]` and`#[derive(GrenatType)]` | \n| `grenat_native` | native facets, Grenat's side: building a facet's library, loading it (ABI checked), declaring and calling its functions | \n| `grenat_bridge` | bridge facets, Grenat's side: a facet's Ruby or Python functions served by a process over JSON-RPC 2.0, described, declared and called | \n| `grenat_sandbox` | sandboxed processes ( `Shell.run` , bridges): an argument vector, a clean environment, no network unless allowed | \n| `grenat_fmt` | the formatter | \n| `grenat_lsp` | the language server | \n| `grenat_macros` | macro expansion: templates of declarations | \n| `grenat_green` | M:N green threads: scheduler, green locks, channels, timers | \n| `grenat_interp` | interpreter: values, evaluation, prompts, agents, budgets, taint, capabilities, workflows, test doubles, evals | \n| `grenat_cli` | the `grenat` binary | \n\nExternal dependencies: `ureq` (HTTP + rustls), `serde_json`, `toml`, `yaml-rust2`, `rusqlite` (SQLite, compiled in), `postgres`, `russh` (SSH), `imap` and `mail-parser` (email in, over `rustls` with `ring`), `lettre` (email out), and Cranelift for native code.\n\nYour choice of [MIT](https://github.com/itsmedit/grenat/blob/main/LICENSE-MIT) or [Apache 2.0](https://github.com/itsmedit/grenat/blob/main/LICENSE-APACHE).", "url": "https://wpnews.pro/news/a-new-ai-agent-oriented-programming-language", "canonical_source": "https://github.com/itsmedit/grenat", "published_at": "2026-10-07 20:09:43+00:00", "updated_at": "2026-10-07 20:20:09.303407+00:00", "lang": "en", "topics": ["ai-agents", "developer-tools", "ai-tools", "agent-protocols", "ai-safety"], "entities": ["Grenat", "basics.grn", "support_desk.grn", "triage.grn", "MCP"], "also_reported_by": [], "alternates": {"html": "https://wpnews.pro/news/a-new-ai-agent-oriented-programming-language", "markdown": "https://wpnews.pro/news/a-new-ai-agent-oriented-programming-language.md", "text": "https://wpnews.pro/news/a-new-ai-agent-oriented-programming-language.txt", "jsonld": "https://wpnews.pro/news/a-new-ai-agent-oriented-programming-language.jsonld"}}