AI Agent Runtime
Overview
An agent runtime that gives Large Language Models (LLMs) from 'Anthropic' https://www.anthropic.com/, 'OpenAI' https://openai.com/, 'Moonshot' https://www.moonshot.ai/, and 'Ollama' https://ollama.com/ direct access to a live R session with managed workspace state. Tools execute as R function calls with provenance tracking, and a deterministic retrieval system keeps relevant objects in context across turns. Three entry points: a shell command-line interface (CLI), a console read-eval-print-loop via chat(), and a Model Context Protocol (MCP) server via serve() for external clients.
New
- **
openai_compatibleprovider for generic OpenAI-compatible endpoints** (#149). Point corteza at OpenRouter, DeepSeek, DeepInfra, a local proxy, or a corporate gateway via abase_urlconfig key or the--base-urlCLI flag (falling back toOPENAI_COMPATIBLE_BASE_URL). The model id is passed through untouched and is required; the API key comes fromOPENAI_COMPATIBLE_API_KEYorOPENAI_API_KEY, and a keyless gateway works with neither. A missing endpoint or model is reported at session setup rather than at the first request.turn()applies the endpoint throughllm.api::llm_base()scoped to each agent call, so a mid-session/providerswitch or a subagent on another provider cannot inherit it. Requiresllm.api >= 0.1.9. Seevignette("configuration").
Show all changes in 0.7.1Hide the rest of the changes in 0.7.1
- **Provider-native web search.** The agent loop enables
llm.api::agent()'s server-side web search for the hosted providers that support it (anthropic,anthropic_claude,openai,openai_codex,moonshot). On by default, no Tavily key required; the model searches inside its own turn. Toggle with--web-search/--no-web-search,chat(web_search = ), or aweb_searchkey in.corteza/config.json. The Tavilyweb_searchtool remains the fallback for local models. - **Matrix
/modellists available models and switches by number.** Bare/model(or//modelwhere the client eats single slashes) replies with a numbered menu instead of echoing the current settings: the configured default, the live local Ollama inventory, and any hosted models declared via the newmatrix_configure(models =)key ("model provider"strings, since hosted providers cannot be enumerated remotely)./model 3switches the room session to entry 3, setting model and provider together, so switching from a phone client never needs a typed model name. The typed form/model <name> [provider]is unchanged, and an unreachable Ollama degrades the menu to the configured entries.
Matrix access control
- **Private conversations and invites are restricted to configured operators.** The new
operatorskey lists the Matrix ids allowed a one-on-one conversation with the bot. A room with one human is answered only when that human is an operator; everyone else gets silence rather than a mention-gated session. Invites are gated on the same list, read from the inviter in the strippedinvite_state, and an invite whose inviter cannot be determined is refused. Group rooms are unaffected. Leavingoperatorsunset preserves the previous behavior. - **Reply gating counts humans, not members.** Rooms whose only non-bot member is one human are answered without a mention; rooms with two or more humans require a mention (replies count, since clients put the replied-to user in
m.mentions) or an open engagement window: after the bot answers someone, that person's plain messages keep getting replies for 5 minutes, ending early when they address someone else. Other bot accounts are declared via the newmatrix_configure(bots =)parameter; their messages always require a mention. Room membership is cached per session and refreshed on unknown senders or every 10 minutes, replacing the boot-time DM snapshot that left rooms stuck on stale gating after membership changed. Withbotsunset, behavior matches the old member-count rule minus the staleness; during a members-API outage the gate fails open for human senders instead of going silent.
Matrix fixes
- **Non-triggering messages are ingested instead of dropped.** In a room with more than one human, a message that did not warrant a reply was marked seen, given a read receipt, then dropped with a bare
next: never appended to the session's history and, because it was already marked seen, never reconsidered. The agent genuinely never saw other people's non-mention messages in a busy room, even though it had "read" them. Gated messages are now appended to history as context; the reply gate and its bot-loop protection are unchanged, only ingestion is. Turns are attributed with a[sender]prefix in rooms with more than one human (both the live poll and the startup backfill), and known bot senders are labeled even in one-human rooms, so the model can tell speakers apart. A lone-human DM is unchanged. - **Rooms load project context into the room system prompt.**
matrix_new_session()passed a non-NULLsystemtosession_setup()withload_project_context = FALSE, so Matrix bots ran without the project briefing, agent context files,.cortezacontext_files, and package tool docs that other channels receive. The newmatrix_room_system()composes the Matrix identity header withload_context()for the room cwd.
Archiving
- **Archives are built from a Matrix-visible event ledger, not from provider history.** History holds tool calls and tool results that were never Matrix events, so a restarted process (which rebuilds history from the server's visible messages) could not align with what it had archived, and tool-using rooms re-archived their whole backfill on every restart. Sessions keep a transcript of
{event_id, role, content}and a queue of pending events, written where events are seen or sent and drained once archived. Archival keys on Matrix event ids, recognized against a bounded per-room tail of already-archived ids. Transcripts no longer contain tool turns. - **Archive progress is persisted per room instead of tracked in memory.**
matrix_archive_session()usedingested_through, an index into the in-memory session history, which reset on every restart while startup backfill refilled history from the server. Each restart therefore re-archived the backfilled tail. Progress is now an ordered key tail persisted per room underCORTEZA_STATE_DIR, advanced only after a successful ingest, so a failed archive retries rather than loses turns. - **Archives are keyed by room id, not display name.** Room display names and avatars are mutable Matrix state, so archived transcripts use the global Matrix room id for
titleandsource. The room name is retained only as archive-time metadata, which keeps renames from splitting one chat into multiple raw archive identities.
From NEWS.md in the release
Install
Health
- OK13 OK2026-08-05
- NOTE12 OK, 1 NOTE2026-08-01
- OK13 OK2026-05-29
- ERROR12 OK, 1 ERROR2026-05-26
- OK13 OK2026-05-16
Show 2 earlier changesHide 2 earlier changes
- ERROR12 OK, 1 ERROR2026-05-14
- OK6 OK2026-05-05
Documentation
- Help pages
- 88 (59 internal)includes a package overview page
- Examples that run
- 45%13 run in a plain check (1 of them under a condition) · 1 only when donttest examples are enabled (R CMD check --as-cran) · 15 never run
- Exported functions with examples
- 38%
- Documented parameters
- 97%
- Return-value docs
- 97%
- Help pages citing references
- 0%
- Configuration HTML
- Package as Skill HTML
- Retroactive-Extraction Runtime HTML
- Skills HTML
- Using Codex with corteza HTML
Downloads
Repository
- PRs merged (all time)
- 97%
- Median days to close, 50 most recently updated closed issues
- 10
- Median days to close, 50 most recently updated closed PRs
- 0
Stars over time
Forks over time
Issues over time
PRs over time
Commits over time
3 development-tooling and community-health practices detected across 2 families in the upstream repository
Checks run against github.com/cornball-ai/corteza on 2026-10-21.
The R CMD check workflow runs on Linux and macOS
This repository shows signs of AI coding tools.
- Claude Codefirst seen 2026-01-29Seen in the repository: CLAUDE.mdNamed in .Rbuildignore: .claude, CLAUDE.mdPull request it wrote: opened by a project memberCommits: at least 43 crediting it in the message
- Claude CodePull requests: 3 from outside contributors, first on 2026-05-07, 1 from forks
- Claude Opus 4.540 commits · Jan to Feb 2026
- Claude Opus 4.61 commit · Feb 2026
- Claude Opus 4.81 commit · Sep 2026
- Code 2.11 commit · May 2026
Dependencies
Code & Tests
People & History
Pick two releases to compare their code metrics.
- 0.7.1Latest
- 0.7.0
- 0.6.92026-05-28 · diff ↗ ·
removed 4 exports
cli_worker_drain_events, cli_worker_spawn, worker_dispatch, worker_tool_listadded 19 exports
context_limit_for_model, context_usage_pct, corteza_execute_in_chat, corteza_execute_in_chat_retain, default_provider_model, estimate_history_tokens, estimate_live_context_tokens, estimate_text_tokens, estimate_tool_tokens, format_age, format_live_ctx, format_tokens, subagent_collect, subagent_seed_history, subagent_turn_set_id, tool_collect_subagent, tool_exit_plan_mode, tool_task_create, tool_task_update - 0.6.32026-05-04
- RR 4.6.0 released · 2026-04-24
Package metadata
- First published
- 2026-05-04
- Total releases
- 4 / 1 yrs
- License
- Apache License (>= 2)
- Minimum R
- ≥ 4.4.0
- Download size
- 944 KB
Cite
Cite this package
Run in R for the authors' preferred citation:
citation("corteza")This package has no citation file, so citation() returns this entry built from DESCRIPTION.
Cite the R Observatory
For a number measured here: a download total, a coverage figure, an archival date.
From data release v2026-10-24, which the citation names so these numbers can be found later. More on citing and the projects behind them.