Paul Furgale Severin Klingler James Nolan Matt Staats par Gaia Di Lorenzo Elisa Martinez Abad Christian Schüller Razvan Dinu par Alessio Devoto Pascal Berard Gal Kaplun Elad Sarafian par Riccardo Roveri Leon Derczynski Ricardo Silveira Cabral par GitHub nvidia-nemo/labs-OO-Agents
Abstract
Traditional agent development is split across prompt templates, tool schemas, callback code, and workflow graphs. We present NVIDIA Object-Oriented Agents ($\textsc{NOOA}$ or NVIDIA double-O Agents), a model-agnostic Python framework for building reliable AI agents. $\textsc{NOOA}$ takes a simpler approach: an agent is a Python object. Its methods are the actions the model can take, fields are its state, docstrings are its prompts, and its type annotations are contracts. A method with code body consisting of in linecode... is completed at runtime by an LLM-driven agent loop, while methods with normal bodies remain standard deterministic Python. This gives developers and agents the same interface, so agent behavior can be tested, traced, refactored, and improved just like other software. This paper makes three contributions. (1) We present the agent-as-a-Python-object programming model and the design principles behind it. Where Python has existing abstractions, we adopt them directly: agents are classes, capabilities are methods, type annotations are contracts, asynchronous work is in linecodeasyncio, and tools and orchestration are normal Python code. Agent-specific capabilities -- context, events, state rendering, long-term memory, and validated LLM loops -- are exposed through simple Pythonic APIs, so both developers and agents share one familiar programming model. (2) We identify six model-facing ideas that $\textsc{NOOA}$ is, to our knowledge, the first to combine on a single surface: typed input/output,pass-by-reference over live objects, code as action, programmable loop engineering, explicit object state, and model-callable harness APIs for context and events. Surveying fourteen agent frameworks and harnesses, we find the community already converging on several of these ideas -- often as experimental or partial features -- and we present the comparison to encourage further adoption. (3) We demonstrate that current models use this interface effectively, both in targeted capability tests and on SWE-bench Verified and Terminal-Bench 2.0; on the ARC-AGI-3 interactive-reasoning benchmark, the interface compresses a multi-agent world-model system into a single agent with a one-page skill while advancing the benchmark's score--cost Pareto frontier.
Executive Summary: NVIDIA Object-Oriented Agents (NOOA) addresses a growing problem in AI agent development. Current toolkits split agent logic across prompt templates, tool schemas, callback code, and workflow graphs, forcing developers to learn new abstractions that models also struggle to use reliably. This fragmentation raises costs, limits testing and reuse, and slows progress on practical agent systems.
The paper introduces NOOA as a model-agnostic Python framework that treats an agent as a single Python object. Methods define actions the model can invoke, docstrings serve as prompts, type annotations act as contracts, and ordinary Python code handles deterministic work. Ellipsis-bodied methods trigger LLM-driven loops at runtime while preserving standard Python interfaces for everything else. The framework was evaluated through capability tests on ten models, end-to-end runs on SWE-bench Verified, Terminal-Bench 2.0, CyberGym L1, and ARC-AGI-3, and a structured comparison against fourteen other agent systems.
Key results show current models already understand and use the interface effectively, passing 97.9 percent of targeted capability tests. On SWE-bench Verified, NOOA reached 82.2 percent with GPT-5.5 and 79.8 percent with Claude Opus 4.6, outperforming comparable open harnesses while using fewer tokens. On Terminal-Bench 2.0 it led or matched the field in most configurations. On ARC-AGI-3, a single NOOA agent with a 50-line skill and the framework’s memory system advanced the score-cost frontier, achieving 85.1 percent at low per-game cost and outperforming a multi-agent baseline. No other evaluated system combines all six core capabilities NOOA exposes: typed input/output, pass-by-reference over live objects, code as action, programmable loop engineering, explicit object state, and model-callable harness APIs.
These outcomes matter because they return agent development to familiar software-engineering practices. Developers and models share one interface, enabling standard testing, tracing, refactoring, and optimization. Reduced context usage and validated termination improve reliability and lower token costs, while the design narrows the performance gap between open frameworks and specialized closed systems.
For next steps, organizations should evaluate NOOA on internal workflows that involve code modification, terminal interaction, or long-running reasoning tasks. Further work should target automated agent rewriting, richer long-term memory, and reinforcement learning over complete trajectories to strengthen disciplined multi-step behavior. Sandboxing around the agent process remains essential for security. Results rest on specific benchmark tasks and current-generation models; broader deployment will require additional validation on proprietary data and production constraints.
1. Introduction
Section Summary: Many existing AI agent toolkits scatter code across prompts, configuration files, and custom orchestration logic, forcing developers to learn new programming models even for tasks that standard Python already handles well. NVIDIA Object-Oriented Agents (NOOA) instead follows the PyTorch approach of offering a simple, familiar Python interface, where a complete agent lives in a single class whose ordinary methods supply state and tools while special “agentic” methods marked by an ellipsis are executed as LLM-driven loops. This design makes agents both easier for humans to build and test and more immediately usable by coding models, with the rest of the paper detailing its principles, implementation, and evaluation.
With the increasing interest in AI agents, there has been a proliferation of agent development kits, each with its own developer-facing and model-facing abstractions [1, 2, 3, 4, 5]. These systems expose useful primitives – tools, memory, workflows, handoffs, traces, and code execution – but they often split agent source code across prompt templates, schemas, callbacks, configuration files, and orchestration code. Consequently, learning a new agent framework often means learning a new programming model for capabilities that already have mature equivalents in ordinary programming languages: typed interfaces, variable scoping, control flow, asynchronous execution, and object state. These abstractions are not only familiar to developers, but also broadly represented in model training data.
NVIDIA Object-Oriented Agents ($\textsc{NOOA}$) is inspired by PyTorch [6], which showed that a powerful runtime can still present users with a simple Python programming model. $\textsc{NOOA}$ applies the same concept to agents: where Python already has the right abstraction, $\textsc{NOOA}$ uses it. Agent actions, helper logic, and harness extension points are ordinary Python programs, familiar to developers, close to the distribution of code that LLMs were trained on, and thus directly understandable by coding agents. Where agent-specific concepts do not already have a standard Python form – for example context construction, event history, and model-visible state – $\textsc{NOOA}$ exposes them as simple Pythonic APIs. This design provides a dual benefit: it eliminates the learning curve for humans and ensures immediate agent readiness.
A complete agent is a single Python class, as shown in the support-agent example below. The class combines object state, deterministic code and two agentic methods: a single-shot in linecodePredict method and an iterative in linecodeCodeAct method.
The class is simultaneously source code, prompt surface, type contract, tool interface, and state boundary. A method with code executes as ordinary Python; a method whose body contains an ellipsis ( in linecode...) becomes an agentic method, so the harness runs it as an LLM-driven loop.
The method declaration specifies the loop: the signature gives the model structured inputs and an output-validation contract, the docstring becomes the prompt, and methods on in linecodeself and imported libraries become callable tools. Note that inputs are not limited to text, as in linecodetriage receives an image and a live in linecodeOrder object, passed by reference rather than serialized into the prompt. This brings prompt engineering back into software engineering, so behavior can be tested, traced, refactored, versioned, and optimized.
The rest of the paper develops this design. Section 2 presents the design principles. Section 3 shows how they are realized in the programming model and harness. Section 4 tests whether current models can use this interface, with capability tests and results on SWE-bench Verified, Terminal-Bench 2.0, and ARC-AGI-3. Section 5 compares fourteen other frameworks and harnesses against the six interface capabilities. Section 6 situates the capabilities in the broader literature and Section 7 discusses limitations and future work.
2. Design Principles
Section Summary: The design of NOOA rests on five core principles that shape how agents are built and run. It reuses familiar Python constructs such as classes, methods, type hints, and asyncio so that developers and models work with ordinary code rather than new languages or tool formats. Agent loops are presented as regular typed method calls, deterministic logic stays in standard Python functions, models act directly by writing code they already understand, and internal harness features like context management are made available through explicit, easy-to-use APIs.
Five principles guided the design. Each principle materializes as one or more interface capabilities implemented by $\textsc{NOOA}$. In the following, we mark each design principle with a square (), and use a callout to name the corresponding
. The principles state the design commitments behind $\textsc{NOOA}$, the capabilities name the concrete model-facing features reused throughout the implementation.
P1. Reuse Python abstractions
If a mature Python abstraction already exists, adopt it rather than introducing a domain-specific language (DSL). In $\textsc{NOOA}$, classes define agents, methods define capabilities, fields hold explicit, model-visible durable state, type annotations define contracts, asyncio expresses concurrency, exceptions signal failures, and control flow is ordinary Python available to developers and agents alike.
$\textsc{Loop engineering.}$ Control flow for single and multi-agent orchestration is ordinary Python.
$\textsc{Object state.}$ Durable state is stored on the agent object, rather than only in conversation history.
P2. Reframe agentic loops as method calls
The application sees an agentic loop as a normal Python method call with typed input/output, not an unstructured text exchange. Arguments are passed by reference as live Python objects, while the harness renders bounded previews and context to the agent, injects arguments and object state into the loop, and validates return values before returning to the caller.
$\textsc{Typed I/O.}$ Agentic methods have typed inputs and typed return values.
$\textsc{Pass by reference.}$ The model operates on live Python objects by reference.
P3. Move deterministic work out of the agentic loop
LLMs are useful for semantic judgment, synthesis, and open-ended tasks. Exact rules, arithmetic, parsing, and state transitions belong in deterministic methods. The boundary is local and visible in the code: a real method body for deterministic work, an ellipsis ( in linecode...) body for agentic loops.
P4. Unlock the model's existing Python knowledge
LLMs already know how to write Python and use popular Python libraries. By letting models write normal Python code instead of tool calls, $\textsc{NOOA}$ draws on that knowledge. CodeAct code can use ordinary loops and conditionals, in linecodeasyncio for concurrency, database clients for queries, plotting libraries for visualization, and ordinary imports for extension – without bespoke prompting, reading documentation, or learning a new DSL. This makes $\textsc{NOOA}$ exceptionally easy to use while maximizing agent readiness, ensuring that the library is as intuitive for autonomous coding agents to build with as it is for human developers.
$\textsc{Code as action.}$ The model acts by writing Python code, control flow and method calls directly.
P5. Expose the harness as explicit APIs
Agent-specific concepts – structured context, context rendering, and event history – are exposed as Python APIs to developers and the model. Where possible, the interfaces mirror built-in types or existing libraries so they are familiar and obvious. The Agent has access to its own context and is able to manage it via Pythonic primitives.
$\textsc{Harness APIs.}$ Harness and Context are exposed through explicit APIs both to the user and to the agent.
3. Agent Loop
Section Summary: A NOOA agent is a Python object mixing ordinary methods with special "agentic" ones whose ellipsis bodies trigger LLM loops instead of normal execution. The harness implements these loops through strategies that render static and dynamic context plus an event history, call the model, execute any resulting Python actions in a REPL, record outcomes, and validate returned values against the method's type signature before handing control back. This keeps overall program flow as standard Python while letting the model iteratively work with the agent's own state and methods until a valid result is produced.
A $\textsc{NOOA}$ agent is a Python object that exposes model-callable behavior through typed methods, fields, and docstrings. Developers write and use this object as ordinary Python code. At runtime, the harness executes regular methods directly and implements ellipsis-body methods as LLM loops. This section unrolls that loop: context rendering, pass by reference, Python execution, event and state recording, and return validation.
3.1 Agents and Strategies
An agent may contain both ordinary Python methods and agentic methods – methods whose body contains the ellipsis literal, in linecode.... Control flow remains ordinary Python until execution reaches an agentic method; at that point, the harness implements the method as an agent loop. The docstring and method arguments become the prompt for the current task, the type signature defines the input and output contract, and the model may use the methods and state on in linecodeself before returning the result. The support-agent example in Section 1 shows both kinds of method in one class.
Strategies
$\textsc{NOOA}$ implements agentic methods through strategies. A strategy is declared as a decorator: it preserves the method's ordinary Python signature and typed boundary, but controls its agentic execution – what context is rendered, how turns are executed, and how candidate outputs are validated. Strategies are per-method, and they are an extension point: new strategies can be added as the field progresses. The decorator also takes per-method overrides – model, truncation, and scoped context – so, for example, a small fast model can serve a classification method while the agent's default model serves open-ended ones. Within a single agent, externally initiated calls to agentic methods are serialized, so independent invocations do not interleave their turns. Nested same-agent calls follow stack discipline: the caller is suspended until the callee returns, and both executions append to the same event history. Other methods, and other agents, run in parallel under Python's standard async/await concurrency model. $\textsc{NOOA}$ provides two built-in strategies:
in linecode**PredictStrategy** is a single-shot strategy for classification or extraction: it renders the context, asks the model for a value, then validates the output against the Python return type, running a local retry loop if the output fails validation.
in linecode**CodeActStrategy** generalizes the same contract into an iterative Python Read-Eval-Print Loop (REPL). The model may call in linecodeexecute_python(...) to compute, inspect internal agent state, call helpers, or invoke other generation methods; the harness records the observation, re-renders the updated state, and repeats until the model calls in linecodereturn_result(...) with a value that is type validated.
The same agent can mix both strategies, choosing per method whichever execution mode fits the task: in the support-agent example, in linecodeclassify_ticket uses Predict and in linecodetriage uses the default CodeAct.
Figure 2 shows the agent loop for the CodeAct strategy. The rest of this section follows the loop: the harness first renders context from the method call (Section 3.2); it then calls the LLM (Section 3.3); if the model chooses a code action, the harness executes Python in the method's REPL session (Section 3.4); finally, it updates events and state with the code output, errors, return values, and locals before the next turn is rendered (Section 3.5). When the model submits a result, the harness validates it against the return type (Section 3.6); failures return an error message to the model, and success returns control to the caller.
3.2 Context
The first step in a CodeAct turn is to render the live Python execution state into model context. $\textsc{NOOA}$ separates context into three regions (see Figure 3): static context blocks, which are computed once and reused across turns; event history, which records the execution trace accumulated so far; and dynamic context blocks, which are re-evaluated before each model call.
Static and Dynamic Blocks
These are developer-controlled, named, structured pieces of text rendered into the model's context window. Static blocks hold information that stays stable across the call, such as the system prompt. Dynamic blocks hold information whose value changes as the program runs, such as a TODO list or selected relevant fields on in linecodeself.
Event History
This is an append-only sequence of typed events produced by the harness as execution proceeds: model tool calls, Python outputs, and return values. Each event is a typed Python object with a unique tag, so agent code can query prior events rather than scanning a flat transcript. Long histories can be collapsed into summary events, akin to MemGPT's context management [7]; strategies can restrict which events are visible to a nested call, and the full event history remains searchable after summarization. Together, blocks and events form the model context of an agentic method.
Context management is therefore not an external prompt-building script; it is part of the same object-oriented API used by the agent. Both the developer and the agent can interact with the context through Pythonic APIs, as shown in Figure 4.
$\textsc{NOOA}$ starts with defaults that make simple agents work well, while still allowing developers to dynamically override every context block at any time. The default static prefix contains a small $\textsc{NOOA}$ system prompt (about 1k characters), the active strategy instructions (about 2.5k characters for CodeAct), an execution-context block showing imported types and libraries, and a concise in linecodedoc(self) rendering of the agent API. The dynamic suffix contains compact views of live agent state ( in linecodepprint(self)). The helper in linecodedoc() provides documentation for types, while in linecodepprint() formats values and instances. Unless scoped by a strategy or method, the event-history block renders the visible execution events accumulated so far.
Rendering context
These three sources are maintained by two programmable objects, shown in Figure 3: the in linecodeContextManager, which stores static and dynamic context blocks, and the in linecodeEventManager, which stores the event history as an ordered log of typed events. The renderer maps these sources into LLM API messages (e.g., OpenAI chat messages). Static framework blocks, such as in linecode<system_prompt> and in linecode<self> (the agent's own in linecodedoc() rendering), are concatenated into a cacheable system prefix visible at every turn. The event history becomes the interleaved user, assistant, and tool messages that record execution: system-generated task messages, agent in linecodetool_call s, and Python output. Dynamic blocks are re-rendered every turn into a trailing user in linecode<context> message. Each dynamic block shows its expression to the model (e.g., in linecodeexpr="self.todo.status()"), reinforcing that this is live state. This three-region layout is designed to maximize KV-cache reuse across turns: the static prefix remains unchanged, the event history grows only by appending new messages, and volatile dynamic blocks are placed at the tail. As a result, updates to live state do not invalidate the cached prefix, and each turn can reuse most of the previous computation.
By default, context blocks and events are wrapped in XML-like tags and events are rendered as typed Python in linecodereprs, as shown at the bottom of Figure 3. Media arguments -- images, audio, video, and files -- are rendered as native multimodal content blocks rather than text, which is how in linecodetriage in the intro example receives its in linecodephoto. The renderer is an extension point: developers have full control over what goes in the context and how it is rendered.
Pass by Reference
Rendering context does not mean serializing the whole program state into the prompt. A CodeAct method receives its arguments as live Python objects, and for large arguments the model never sees the full value. In the spirit of progressive disclosure [8], the model sees each argument's variable name paired with a bounded preview: the concrete type, the true length, and a short head/tail sample. The model reads that shape, understands that the name refers to a real object, and operates on it directly in generated code.
For example, a method called with a list of one hundred integers renders in the prompt as a single compact preview:
The preview states the concrete type ( in linecodelist), the true length ( in linecodelen=100), and a head/tail sample; the elided middle is implied. The variable in linecoderecords itself is not truncated -- it is the full hundred-element list bound as a local in the execution environment -- so the model can index, slice, or iterate over all of it ( in linecodefor r in records: ...) even though only ten elements ever appear in the context window.
This is what lets the object model scale past the context window: the amount of data an agent can process is bounded by the execution environment, not by the prompt. A method can accept a multi-million-row table or a multi-megabyte string and the agent works on the whole thing by writing code, while the prompt carries only a fixed-size preview.
Python has no standard library for truncating arbitrary values. The closest is Rich's in linecodepprint() [9], so we borrowed its name and API surface – both are in the model's training data – but changed the output format based on experimentation across open and closed models. Finding even better formats that are obvious to LLMs, and supporting more types, remains open work.
Methods using the Predict strategy render argument values in full, guarded by a size cap: a Predict call is a single LLM call, so the model has no opportunity to inspect a variable.
3.3 Calling the LLM
Once the harness has rendered the current turn, control passes from Python to the model. The LLM receives the structured context assembled in the previous step, together with the strategy-specific contract for what it may do next. Under in linecodePredictStrategy, the model must produce a value matching the return annotation. Under in linecodeCodeActStrategy, the model must choose between continuing computation with in linecodeexecute_python(...) or terminating the method with in linecodereturn_result(...).
3.4 Executing Python
When a CodeAct model chooses a Python action, $\textsc{NOOA}$ executes the cell in a restricted, Jupyter-like session. Method arguments, the live agent as in linecodeself, and the agent's environment (imports, methods, and constants defined in the agent's source file) are injected as locals; in linecodeawait can be used directly. The cell can inspect objects with in linecodedoc(obj), print bounded previews with in linecodepprint(), call deterministic helpers, await generation methods, spawn subagents, or return an in-process Python value with in linecodereturn_result(...).
This is the second half of pass by reference mentioned in Section 3.2: the model writes code against real objects rather than serialized tool arguments. All tool calls are strongly typed and pass by reference in both directions, so the agent can call a method with a huge input, bind the huge typed result to a variable, and process it programmatically – slice it, aggregate it, feed it to the next call – while only the bounded previews it chooses to print enter the context window. Models already improvise this pattern in bash – spilling results to files and processing them with follow-up commands; $\textsc{NOOA}$ replaces the untyped text on disk with typed, live variables that persist from cell to cell.
Dangerous or loop-breaking APIs such as in linecodeeval, in linecodeexec, in linecodecompile, in linecodeinput, and blocking event-loop calls are rejected with specific errors. Stdout, stderr, images, returned values, locals, and exceptions are captured as structured results. Syntax errors and tracebacks are in IPython format, including source locations and caret/source-line context, so the next LLM turn can repair the code the way a human would repair a notebook cell.
Cells can contain loops, conditionals, library calls, async operations, helper calls, and subagent invocations. This gives the model the same orchestration tools as the developer: inside a cell, it can define a new in linecode@strategy-decorated function with an ellipsis body and fan it out over a batch with in linecodeasyncio.gather, creating parallel subagent calls in ordinary Python.
3.5 Updating Events and State
After every model response or Python execution, the harness appends typed events to the event manager: tool calls, Python outputs, and final return values.
State updates follow standard Python scoping rules. REPL locals are method-scoped – they persist across cells within a single CodeAct call and then disappear when the method returns – so intermediate values stay local to the task. Anything reached through in linecodeself or through library calls, by contrast, can have side effects that outlive the method, exactly as they would in an ordinary Python program.
3.6 Validating the Return
When the model returns a result, the harness validates it against the return annotation. If the result is invalid, the harness sends the model an error message describing the failure, and the loop continues. If the result is valid, the harness returns it to the caller and normal Python execution resumes.
3.7 Long-Term Memory: the Agent Curates Its Own State
The mechanisms described so far are scoped to a method call or a session, yet an agent with frozen weights can only improve through the state it retains. Our companion work on workspace optimization [10] shows that agents can learn by writing typed, evidence-gated artifacts in place of parameter updates; its principal open problem is transfer, because the workspace is discarded at every task boundary. $\textsc{NOOA}$ addresses transfer with an optional long-term memory subsystem: in linecodeMemoryManager.install(agent) attaches it to an unmodified agent, and uninstalling restores the agent exactly.
The agent authors its own memory
Following Principle 5, writing a memory is a deliberate action of the model rather than the output of a background extraction pipeline. Seven model-callable tools ( in linecoderemember, in linecoderecall, in linecodesearch, in linecodeupdate_memory, in linecodeforget, in linecodeassociate, in linecodederef) operate on the store; they accept ordered verbal descriptors ($\textsc{critical} \ldots \textsc{trivial}$) that map to numeric scores internally, and a standing context block states that the store is the agent's own to maintain.
Deliberate and spontaneous recall
Memory reaches the model through two channels: the agent queries the store with its tools, and a in linecodeBeforeTurn hook derives a query from recent events and injects associated memories into a dynamic context block. Injected memories are not reinforced, so what the harness surfaces does not distort the usage signal. Retrieval unions embedding and keyword candidates, ranks them by ACT-R activation [11] – relevance, recency, and importance, the triad of generative agents [12] – and propagates activation over a typed memory graph. Decay-based forgetting keeps the store bounded.
Asynchronous reflection
Consolidation runs outside the agent loop, after a task completes or while the agent is idle, as an ordered pass: near-duplicate memories are merged; conflicting values can be reconciled into a single current record, archiving the superseded ones; related memories are linked; importance is re-scored; episodes can be distilled into higher-level records; and memories whose activation has decayed are pruned. Pruning never removes recent memories, protected types, or open todos.
One inspectable file; live references
The entire store is one SQLite file that can be inspected directly; vector indexes are derived from it and interchangeable. A memory may hold typed references ( in linecodekind:key) that are resolved against live agent state at recall time – extending pass by reference into persistence, so recall does not answer from stale copies – and owner scoping governs reads and writes when several agents share one store. The subsystem's end-to-end effect is measured in Section 4.5: +11.8 RHAE points over the identical agent with file-based notes in place of memory. Figure 5 shows the architecture; Appendix C details the design and compares memory support across contemporary harnesses.
4. Evaluation
Section Summary: The evaluation of NOOA proceeds at two levels. Targeted tests first check whether current AI models can correctly interpret and use the system's interface features, such as calling methods, handling state, and returning properly typed results; across 4,400 runs on ten models, the tests passed at a 97.9% rate, with larger models performing especially well. The authors then assess complete NOOA agents end-to-end on established benchmarks covering software engineering, terminal use, cybersecurity, and interactive reasoning to measure real-world effectiveness.
We evaluate $\textsc{NOOA}$ at two levels. First, in Section 4.1, we use targeted capability tests to determine whether current models understand and correctly use the abstractions exposed by the $\textsc{NOOA}$ interface. Second, we evaluate complete $\textsc{NOOA}$ agents end-to-end on benchmarks spanning software engineering and terminal interaction (Section 4.3), cybersecurity (Section 4.4), and interactive reasoning (Section 4.5).
4.1 Capability Tests: Do Models Understand the NOOA Interface?
Experimental setup
We built a suite of focused integration tests that isolate one interface behavior at a time. The question is not only whether a model can solve a task, but whether it can call helper methods, write executable cells, interpret bounded variable previews, manage state, and return typed values through the harness. The suite contains 88 test instances across 36 families, covering typed method calls, structured returns, stateful object manipulation, routing to helper agents, context and truncation handling, REPL and code execution, batching through generated loops, error recovery, and task decomposition.
Most tests are short interactions of one to five turns. The harder cases stress bookkeeping over batches, recovery after errors, multi-step REPL exploration, and the implementation of reusable helper methods. The complete suite is included in the $\textsc{NOOA}$ repository. We run each test five times for each of ten models, yielding 4, 400 records in total.
Models understand the interface
Table 1 shows that current generation models are generally fluent in the $\textsc{NOOA}$ interface; the suite passes 4, 309 of 4, 400 records (97.9%). We group models by scale: four small/efficient models (Claude Haiku 4.5, Gemini 3.5 Flash, Nemotron 3 Nano 30B, GPT-5.4 Mini) and six large/frontier models (Claude Opus 4.8, Gemini 3.1 Pro, GLM-5.2, Kimi K2.6, Nemotron 3 Ultra, GPT-5.5). Small/efficient models pass 96.0% of records; large/frontier models pass 99.2%. Every model exceeds 91%, and six of ten models exceed 98%. GPT-5.5 is perfect on this suite, while Gemini 3.5 Flash and GLM-5.2 miss only one test each. Capability-suite pass rates discriminated by the use of reasoning show frontier models saturating regardless of mode (Opus 100.0/99.5, GPT-5.5 99.5/98.6, off/on), while the value of reasoning grows monotonically as model capability falls — Ultra 93.4 → 94.1, Super-v3 83.7 → 96.4, Nano 52.5 → 84.8 — making inference-time reasoning a capability equalizer for the smaller Nemotron models.
The important implication is that the interface itself is not a burden for current generation LLMs. Models know Python; they can read object documentation, call methods with typed arguments, use returned values, mutate object state, and return values that satisfy the type contract. This zero-shot fluency validates the framework's empirical agent readiness: by expressing agentic constructs as native software abstractions, we completely remove the interface friction introduced by other frameworks.
Stress tests expose the remaining frontier
The residual failures are concentrated in six stress families, shown in Table 2. These are the tests that most resemble agentic work rather than single tool calls: preserving per-item bookkeeping in a large batch, recovering from errors, iterating in a REPL, refining an intermediate answer, and decomposing repeated transformations into helpers. The stress subset passes 254 of 300 records (84.7%), compared with 97.9% overall. Large/frontier models pass 169 of 180 stress records (93.9%), while small/efficient models pass 85 of 120 (70.8%) – the scale gap widens from 3.2 points overall to 23 points on the stress subset.
Running each test five times also measures consistency. Models are consistent: of the 880 (test, model) pairs, 94% pass all five runs, only three fail all five, and the rest are intermittent. The stress tests separate the two failure modes: large models have no 0/5 scores – every failure is intermittent, a reliability miss on a demonstrated capability. Small models show both, with 12.5% of stress pairs at 0/5 and 42% intermittent.
::: {caption="Table 2: Stress-test pass rates. Each row has 50 records (10 models × five runs), split into four small/efficient and six large/frontier models as defined in the text."}
:::
These are not failures to understand in linecodeself or to call a method; they are failures of disciplined multi-step harness use (Appendix B shows four complete runs of the hardest stress test). This distinction matters: basic interface fluency is already widespread, while reliable long-horizon batching, recovery, and decomposition at the code/model interface remain capability frontiers.
4.2 Experimental Results on Agentic Benchmarks
We evaluate $\textsc{NOOA}$ on four agent benchmarks covering complementary forms of end-to-end interaction. SWE-bench Verified [13] measures software-engineering performance on real repository issues, while Terminal-Bench 2.0 [14] evaluates multi-step interaction in a command-line environment. CyberGym L1 [15] tests an agent's ability to identify and repair software vulnerabilities, and ARC-AGI-3 [16] evaluates interactive reasoning in unfamiliar environments. Together, these benchmarks span code modification, terminal use, cybersecurity, and adaptive problem solving.
4.3 Software Engineering and Terminal Interaction
SWE-bench Verified [13] contains 500 software-engineering tasks derived from issues in real GitHub repositories. An agent must inspect an unfamiliar codebase, identify the cause of a reported problem, modify the repository, and produce a patch that passes the benchmark's tests. Terminal-Bench 2.0 [14] contains 89 tasks performed through a command-line environment, including software installation, configuration, debugging, and service operation.
Agent and comparison harnesses
For both benchmarks, we use the same benchmark-agnostic agent, in linecodeBenchAgent. The agent has a todo list, shell tools for command execution and file editing, and repository-navigation tools based on tree-sitter. Its dynamic context contains the task description, todo-list status, context-window statistics, and the current working state of its shell and repository tools. The agent terminates through a typed in linecodeTaskResult containing the identified root cause, supporting evidence, and a verification command. This return value is validated by the harness before execution ends. The complete agent consists of 253 lines of ordinary Python and is included in the $\textsc{NOOA}$ repository.
We compare $\textsc{NOOA}$ with two open, general-purpose coding agents. OpenCode [17] is a full-featured terminal coding agent with file, search, and shell tools, together with automatic transcript summarization. PI [18] is a deliberately minimal agent with a small prompt, standard file and shell tools. All three harnesses are evaluated with the same GPT-5.5 and Claude Opus 4.6 backends at the available reasoning-effort settings. We report task pass rate in Table 3 and Table 4, and per-task token usage in Figure 6.
Results
On SWE-bench Verified, $\textsc{NOOA}$ obtains the highest pass rate among the open harnesses in every evaluated model and reasoning configuration. Results show that NOOA improves on the original CodeAct paradigm as implemented by OpenHands v3 [19], which under Opus 4.6 is reported to have a 68.4% pass rate. $\textsc{NOOA}$ builds on this result by 11.4 points, given the same model. With GPT-5.5, it reaches 67.2%, 78.8%, and 82.2% at off, high, and xhigh reasoning effort, respectively. At xhigh effort, OpenCode reaches 78.6% and PI reaches 78.2%. With Opus 4.6, $\textsc{NOOA}$ reaches 79.8%, compared with 75.2% for OpenCode and 75.8% for PI.
The advantage is larger on Terminal-Bench 2.0. With GPT-5.5 and reasoning disabled, $\textsc{NOOA}$ reaches 46.1%, compared with 34.8% for OpenCode and 37.1% for PI. At high effort, it reaches 73.0%, ahead of OpenCode by 12.3 points and PI by 4.5 points. PI obtains the best GPT-5.5 xhigh result at 75.3%, compared with 73.0% for $\textsc{NOOA}$. With Opus 4.6 at high effort, $\textsc{NOOA}$ reaches 65.2%, while OpenCode and PI reach 43.8% and 58.4%, respectively.
The higher pass rates do not come from using longer trajectories. On SWE-bench with GPT-5.5 xhigh, $\textsc{NOOA}$ reaches 82.2% using approximately 28 model calls and 1.1 million tokens per task. OpenCode uses a similar number of calls but approximately 1.3 million tokens for 78.6%, while PI uses 66 calls and 2.2 million tokens for 78.2%. As shown in Figure 6, $\textsc{NOOA}$ therefore defines most of the observed accuracy–cost frontier.
Effect of reasoning effort.
Increasing reasoning effort improves all three harnesses, but the interface matters most when the model provides less planning and verification discipline of its own. With reasoning disabled, $\textsc{NOOA}$ leads OpenCode and PI by 8.0 and 6.4 points on SWE-bench, and by 11.3 and 9.0 points on Terminal-Bench. These margins narrow at higher effort, suggesting that the explicit object state, typed actions, and programmable loop behavior exposed by $\textsc{NOOA}$ partly substitute for behaviors that stronger reasoning models increasingly perform themselves.
Validated termination.
Trace analysis identifies termination as a key difference between the harnesses. OpenCode stops whenever the model responds without a tool call; on Terminal-Bench, 77% of its failed GPT-5.5 trials terminate within ten steps. In $\textsc{NOOA}$, the model must instead return a validated in linecodeTaskResult containing evidence and a verification command. This prevents unsupported declarations of completion and is especially valuable on tasks whose intermediate state can appear correct before hidden checks are run. More broadly, it illustrates the value of treating type annotations as executable contracts: termination becomes a programmatically validated action rather than an informal convention encoded only in the prompt.
Interaction and context efficiency.
$\textsc{NOOA}$ also uses fewer tokens because tool outputs remain available as live Python values rather than being repeatedly serialized through the transcript. With GPT-5.5 xhigh on SWE-bench, it reaches 82.2% using approximately 28 calls and 1.1M tokens per task, compared with 78.2% using 66 calls and 2.2M tokens for PI. Bounded prompt previews also keep $\textsc{NOOA}$ well below the context limit, avoiding the lossy transcript compaction used by OpenCode and PI while preserving prefix-cache reuse. These results directly expose the benefits of combining code as action with pass-by-reference: the model can operate on persistent objects in the execution environment instead of repeatedly exchanging their full textual representations with the harness.
Comparison with specialized systems.
The results also narrow the gap between open general-purpose harnesses and specialized closed systems. On SWE-bench Verified, $\textsc{NOOA}$ reaches 82.2% with GPT-5.5 and 79.8% with Opus 4.6, compared with 88.7% for Codex and 80.8% for Claude Code. On Terminal-Bench 2.0, its 65.2% with Opus 4.6 is comparable to the 62.9–65.4% reported for Claude Code and Terminus-2. Thus, a small benchmark-agnostic $\textsc{NOOA}$ agent is competitive with specialized systems while consistently outperforming the open general-purpose harnesses in our comparison. This supports the broader agent-as-a-Python-object claim: ordinary classes, methods, state, and type contracts provide a simple developer-facing abstraction without making the interface less effective for models.
CyberGym [15] is a security benchmark, in which an agent must inspect a codebase, identify a security-relevant bug, and validate it by producing a proof-of-concept that reliably triggers it. Agentic vulnerability discovery is notoriously difficult and can consume large amounts of context. Crash reports are long; code bases can be long; and small pieces of information need to be coupled across potentially long distances. We test whether the deconstruction and simplification offered by the $\textsc{NOOA}$ architecture can yield gains in the vulnerability validation stage.
CyberGym $\textsc{NOOA}$ agent
Runs in the trial container as a CodeAct agent with shell and a todo manager tools. The agent reads the task description, investigates the mounted source, writes a PoC, and submits it through the CyberGym submission interface. A deterministic layer around the model keeps the important scoring mechanics out of the prompt loop: a submission method sends the authored proof-of-concept and processes benchmark response; a lightweight judge checks that the model's summary still matches the described vulnerability before accepting; and accepted submissions are re-submitted a few times to reject non-deterministic crashes. No domain knowledge is included beyond this. Performance is predicated on agent architecture rather than cybersecurity steering.
Results
Scores compared to state-of-the-art are given in Table 5. We report a number of leading closed-source results, and include two baselines: OpenAI Codex, and OpenAI Codex plus a skill used to comply with the CyberGym submission format. $\textsc{NOOA}$ scores highly, beating the majority of closed-source solutions, and is the top-scoring open source agent.
Network access
Monitoring network access affects performance. We implemented a rigorous "cheat check" with rule-based analysis of agent trajectories. This ensured that $\textsc{NOOA}$ results are based only in information that the agent is processing and inducing directly from the problem setup, rather than being able to look up information about relevant disclosed vulnerabilities or the benchmark itself online.
: Table 5: Vulnerability discovery performance on CyberGym L1
Harness
Model
Network
Solve rate (%)
Open source?
Microsoft MDASHv2
MDASH
unknown
95.6
No
Crystalline
Opus 4.6
blocked
89.6
No
NOOA
GPT-5.5
blocked
86.8
Yes
OpenAI Daybreak
GPT-5.5
unknown
85.6
No
OpenAI Codex + submission skill
GPT-5.5
open
83.5
Yes
Anthropic Glasswing
Mythos
unknown
83.1
No
OpenAI Codex
GPT-5.5
blocked
64.9
Yes
4.5 Advancing the score–cost Pareto frontier on ARC-AGI-3
ARC-AGI-3 [16] is an interactive-reasoning benchmark: the agent is dropped into an unknown grid game and must discover mechanics, objective, and controls purely by acting. Our companion DreamTeam system [10] – six specialized agents coordinating around a shared executable world model – set the previous best published score on it. We test whether that methodology survives radical simplification: one $\textsc{NOOA}$ agent and one 50-line skill, with six role prompts (1,821 lines) and a 4,690-line harness-side retrodiction engine absorbed by framework primitives – the CodeAct REPL as simulator, context blocks as shared state, memory (Section 3.7) as the team's carry-forward ledgers.
The world-model skill instructs the agent to persist an executable model as workspace modules: in linecodeencode(grid) $\to$ z, a latent of the few fields that drive the game; in linecodepredict(z, action) $\to$ z', the dynamics; retrodiction each turn -- a predict-vs-observed mismatch is the sole refinement signal; search over its own in linecodepredict once trusted; and memory discipline across levels. Every turn ends with in linecodesubmit_actions(..., rationale="predict: ...") – each action batch is a checked experiment.
Results. We ran four 25-game fleets, one agent per game, under the competition's two-hour cap: the world-model skill with the memory subsystem on GPT-5.5 and on GPT-5.6-sol, the same skill with plain markdown files in place of memory, and a hypothesis-driven baseline skill with memory (the last two on GPT-5.5).[^1] Figure 7 plots the fleet-mean RHAE – the competition's action-efficiency score against per-level human baselines – over time and spend. At the cap, the world-model + memory fleet on GPT-5.5 reaches RHAE 50.2% (118 levels), vs. 41.7% for the baseline and 38.4% for the markdown-file ablation: +8.5 points over the baseline, and +11.8 points over the same skill without the memory subsystem. On GPT-5.6-sol the same agent (170 levels) is scoring 85.1% with less than $20 per game; the guarded, cache-aware fleets cost $17.85 (GPT-5.5) and $13.28 (GPT-5.6-sol) per game at gpt-5.5 pricing. For scale, ARC Prize's own evaluation of raw GPT-5.6-sol – the only performant base model on the benchmark as of July 2026 – averages 13.3% on the same 25 public games at maximum reasoning effort[^2]; the same model inside the $\textsc{NOOA}$ harness reaches 85.1% – a $6.4\times$ harness effect. The curves separate once a game's mechanics have been observed enough to encode and predict.
[^1]: Public ARC-AGI-3 scorecards for the two world-model + memory fleets: GPT-5.5 and GPT-5.6-sol.
World-model use. 22 of 25 games persisted executable model code ($\sim$ 4.4k lines). Game m0r0, for example, replayed twenty live frames through its in linecodeencode to validate a stored 42-action plan mid-execution and completed 6/6 levels near the per-level score cap.
Memory use. The fleet exercised all three interfaces of the memory subsystem (Section 3.7; Table 6): 3,262 memories written, 12,654 spontaneous injections, and 27,115 deliberate tool reads at a 99% hit rate. Retrieval favors what the agent marked important (mean importance 6.1 written vs. 7.5 deliberately recalled), injection stays bounded at 4.1 memories per turn, and recall frequency tracks success: winning games average 1.63 deliberate recalls per decision, and recalls per decision correlate with levels completed at Spearman $\rho=+0.52$ (Appendix D.4).
Containment. The fleet runs inside layered sandboxing: a kernel-enforced per-cell OS sandbox (each CodeAct cell in a locked-down worker under irrevocable Landlock filesystem default-deny, a seccomp network block, memory/CPU caps, and a hard cell timeout) over the in-process cell guard, a per-run OS privilege drop, and game identities replaced end-to-end by opaque aliases. An 18-pass red-team audit of the live run found no leakage on any rule – no internet egress, no game-source or cross-game reads, zero real game identifiers in 13,335 logs – and the single escape attempt was blocked by the cell guard. Appendix D details the evidence and the audit.
::: {caption="Table 6: Memory-system use by the ARC-AGI-3 fleet (25 games): what agents wrote vs. what each read channel surfaced. Read columns count occurrences (one memory surfacing once); imp. = mean importance (verbal scale mapped to 0–10); len = mean characters."}
:::
5. Comparison to other harness libraries
Section Summary: This section compares the NOOA system to fourteen other agent frameworks and harness libraries, evaluating them against the same six interface capabilities discussed earlier. While most existing tools offer partial versions of these features, they typically expose them only to developers rather than directly to the AI model, or they wrap them in limited ways. The authors conclude that NOOA is the first to combine all six capabilities in one interface, though they note a broader trend in the field toward adopting similar patterns.
In Section 2, we identified six interface capabilities that $\textsc{NOOA}$ combines: typed I/O, pass by reference, code as action, programmable loop engineering, object state, and model-visible harness APIs. The previous sections showed how these capabilities are implemented in $\textsc{NOOA}$ and how they affect agent behavior. We now compare $\textsc{NOOA}$ with fourteen agent frameworks and harnesses along the same axes. The comparison shows that prior systems support important subsets of these capabilities, but, to the best of our knowledge, $\textsc{NOOA}$ is the first agent development kit to expose all six on a single surface. Table 7 provides an overview.
How we scored the results.
We scored each system by reading its documentation and source code. Every score is checked against a pinned snapshot; the repository, commit, and package version are listed in each system's subsection of Appendix A (snapshots retrieved July 7–9, 2026). Green (Supported) means the capability is a first-class part of what the model sees. Yellow (Partial) means it exists, but mainly for the developer, or behind a tool or a file. Red (Limited) means we found no evidence of it. Experimental, flag-gated, or opt-in capabilities are scored on the capability itself and marked † rather than demoted. For harness APIs the bar is that the model itself can see or call the context and event machinery; tracing dashboards, automatic compaction, and hidden callbacks do not count. Table 7 gives the scores with a short reason per cell; Appendix A has the full evidence.
No other system combines all six ideas, but most are adopting some of them. Most systems have a version of each idea, but expose it to the developer instead of the model, or wrap it in a new abstraction where a mature one already exists. The newest, strongest capabilities – Microsoft's harness providers, Pydantic's CodeMode harness, OpenAI's sandbox agents, Codex's code mode – shipped during our evaluation window, most marked experimental or flag-gated (†). We read this as the field converging on these six ideas.
::: {caption="Table 7: Emerging design patterns across agent development kits and harnesses. The field is broadly converging on six harness-interface patterns; shading shows how each system realizes each pattern today: setlengthfboxsep1.5ptnative, setlengthfboxsep1.5ptemerging, or setlengthfboxsep1.5ptminimal. Detailed evidence appears in Appendix A."}
:::
6. Related Work
Section Summary: This section reviews prior research on agent harness design for large language models, organizing it into categories such as typed input-output interfaces, using executable code as the primary way for models to take actions, and mechanisms for passing live program objects by reference instead of serializing everything to text. It highlights systems like DSPy, CodeAct, and Nightjar that address structured prompting, code execution environments, and shared program state, while noting how many existing frameworks still rely on text-based or file-based exchanges that lose type information. The discussion positions NOOA's six interface capabilities within this landscape of evolving approaches to orchestration, memory, and model-visible operations.
The comparison in Section 5 evaluates existing harnesses against the six interface capabilities implemented by $\textsc{NOOA}$. This section situates those capabilities in the broader literature. We group prior work by the contribution to agent harness design: structured and typed LLM programming, executable code as an action interface, programmable orchestration, state and memory, and model-visible harness operations.
Typed I/O:
Agent calls have typed inputs and a typed return value. DSPy [20] made declarative signatures – named input and output fields, with data types added in later releases – the unit of LLM programming, decoupling declared intent from prompt wording and making pipelines programmatically optimizable. LMQL [21] frames prompting as a query language whose output constraints (including type constraints) are enforced at decoding time, and Outlines [22] enforces output structure during generation by compiling regular expressions to finite-state machines (and grammars to pushdown automata) that mask invalid tokens at each step. Engineering libraries such as Instructor [23] and TypeChat [24] validate model output against a schema and re-prompt with the validation errors. Mainstream agent frameworks have converged on the same need: LangChain agents [1] and PydanticAI [25] accept an output schema, and Google's ADK [26] enforces an output schema on agent replies and additionally accepts an input schema when an agent is exposed as a tool via its agent-as-tool path.
Agentic methods and tools in $\textsc{NOOA}$ are defined as Python methods, and type annotations at generation-method boundaries are enforced by the runtime.
Code as action: The model acts by writing arbitrary code. PAL [27] and Program of Thoughts [28] offload computation to generated programs, and Chain of Code [29] interleaves interpreter execution with LM-simulated execution of the lines an interpreter cannot run. CodeAct [30] consolidates the argument that executable code should be the action modality itself, outperforming JSON and text actions; smolagents [31] packages the paradigm as a library, executing model-written code in a restricted interpreter with tools as callable functions; and OpenHands [19], built on CodeAct, demonstrated the paradigm at scale on software engineering tasks (its V1 SDK rebuild has since moved to discrete shell and file tools; see Section 5) (SWE-Agent [32] showed, complementarily, that the agent–computer interface itself is a first-class design surface). Anthropic's programmatic tool calling has the model invoke tools as functions from within an executing program rather than as one JSON call per turn, keeping bulk intermediate results out of the context window [33]. Recursive Language Models [34] and Recursive Agent Harnesses [35] study long-context decomposition through recursive model or harness calls.
Bash tools are themselves a weak form of code as action: a shell command line is a small program, with pipes and loops for control flow and CLIs as callable tools. We believe this explains the rise of tool-as-CLI over tool-as-MCP packaging – a CLI is called from code, so the model can filter, transform, and compose outputs programmatically, while an MCP tool is a single JSON call whose full result lands in the context window. The shell's limitations remain: it operates only on untyped text, with no variable persistence outside of files.
Recent survey work frames the same shift more broadly as code as agent harness [36]: code becomes the substrate for reasoning, acting, environment modeling, execution-based verification, planning, memory, tool use, and multi-agent coordination. $\textsc{NOOA}$ has realized this design as an object-oriented Python runtime.
Pass by reference: The model operates on live, in-process objects. Most agent frameworks use copy-as-text at every interface. Inputs are serialized to text, tool call inputs are generated as text by the LLM and outputs are returned as text, and finally LLM output text is parsed back into the host language [37]. Cheng et al. [38] propose shared program state as a natural function interface, allowing prompts to read and write live program state via explicit references. Their programming system Nightjar embeds natural-language code blocks inside Python programs, using angle-bracket notation ( in linecode<var> to reference and in linecode<:var> to assign shared variables) and an interface through which the LLM manipulates state. AskIt [39] provides a type-guided domain specific language that turns typed prompt templates into callable functions, serializing captured host variables into the prompt and parsing typed output back. ANPL [40] interleaves user-written Python-like sketches with LLM-implemented natural-language holes. CodeAct [30] replaces the structured tool-calling format with a Python REPL in which tools are ordinary functions and live objects persist across turns; $\textsc{NOOA}$ 's default strategy descends from this paradigm, as does smolagents [31], which injects developer-supplied Python objects into the executor namespace for the model to use by name; Recursive Language Models [34] push reference-passing to its logical conclusion: the prompt itself becomes a variable in a REPL that the model inspects, slices, and recursively queries with sub-model calls rather than reading it in full. TaskWeaver [41] maintains cross-step state as live Python variables.
Many popular frameworks today use files as a variant of pass by reference, giving the agent the filename as input and allowing it to explore it via tool calls. This is powerful, but it loses all type information and requires the agent to reconstruct any types embedded in the file by reading the text.
When $\textsc{NOOA}$ operates in CodeAct mode, inputs and tool arguments are live Python objects in the session namespace, and computed outputs are returned by reference from inside code ( in linecodereturn_result(variable)). Every call begins with an input-inspection step that prints each parameter's type and a size-bounded preview; the live variable remains in the session for further exploration. This keeps context usage small and under the agent's control, and it allows processing of large inputs by shape without the full content ever entering the context window.
Loop engineering: Control flow for single- and multi-agent orchestration is available to developers and the agent. Many popular agent-building frameworks provide developer APIs for orchestrating agents: LangGraph [42] expresses control flow as a graph of nodes that read and write typed shared state, and Microsoft Agent Framework [43] builds workflows as directed graphs of executors exchanging typed messages. Google's ADK [26] pairs developer-defined workflow agents with model-callable control primitives. smolagents injects developer-defined managed agents into the model's code namespace, so the model can write loops over subagents in its own actions. Claude Code's dynamic workflows have the model itself author the orchestration code: given a task, the agent writes a script that fans out tens to hundreds of parallel subagents with verification stages before results are returned [44]. $\textsc{NOOA}$ needs no separate workflow language: outer loops are ordinary Python methods, inner loops belong to the model, and the agent can write and invoke the same control flow the developer does.
Object state: The agent has explicit, model-visible durable state. For most agents, the conversation history is the state. As conversations get long and history is compacted, state can be lost. MemGPT [7] treats the LLM as an operating system that pages information between in-context and external memory tiers. They reserve a fixed-size read/write "working context" section of the prompt that is exempt from eviction, so key facts survive long conversations.
Today's harnesses keep durable state in three ways: model-edited files (memory files like MEMORY.md re-injected at session start, todo and plan stores, AGENTS.md instructions), searchable conversation history (full-text or vector search over past session transcripts), and dedicated memory tools with add/replace/remove verbs over a store (Section 5). All three survive compaction and sessions, but all three hold untyped text outside the model's working state.
$\textsc{NOOA}$ 's state is object-scoped and part of the contract: typed fields and named context blocks live on the agent instance, and public fields are rendered into the prompt from the live object each turn – held out of history eviction rather than reconstructed from a transcript.
Harness APIs: Context blocks, per-turn dynamic context, and event inspection are exposed as model-callable APIs. MemGPT and its successor Letta [7, 45] expose the harness itself as model-callable tools: the model edits its own in-context memory blocks, searches its message history, and pages file content in and out of context – though context compaction remains harness-triggered, with the model only warned of memory pressure. Memory-R1 [46] trains such capabilities with reinforcement learning: a memory-manager agent learns add/update/delete/no-op operations over a memory store while a separate answer agent learns to filter and reason over retrieved memories. $\textsc{NOOA}$ exposes its harness uniformly through the object model: static and dynamic context blocks and the queryable event history are model-callable APIs whose visibility to the model the developer opts into per agent, with scoped overrides available to harness code. We believe that giving agents direct, programmatic access to their context and history will be key to unlocking new agentic behavior on long-running tasks.
7. Conclusion
Section Summary: NOOA is a Python-based toolkit that turns AI agents into ordinary software objects with methods and state, letting developers and models use the same familiar tools and libraries to build and run them. Although it currently runs model-written code directly in the same process, which requires external sandboxing for safety, the approach already works well with today's models. Looking ahead, the authors see potential in letting agents rewrite their own code, treat skills as full software libraries, and use reinforcement learning to improve long-term reasoning, so that progress comes from jointly evolving both the models and the software environments they work in.
$\textsc{NOOA}$ brings together many of the recent advances in agent design into a single ergonomic development kit in which an agent is a Python object with methods and state. This design makes agentic software ordinary software: developers and agents use the same interface, the same libraries, and the same tools. Our evaluation shows that current models already operate this interface effectively, despite never being trained on it.
Limitations: $\textsc{NOOA}$ executes model-written code in the agent's own process. The validator in Section 3.4 protects the agent loop, not the host. In this respect $\textsc{NOOA}$ 's isolation philosophy is the same as any harness with a shell tool: sandboxing – a container, VM, or permission system – goes around the agent process, and a shell tool is no safer than in-process Python; most harnesses in Section 6 ship one. Executing in-process is what preserves pass by reference; sandboxed code modes trade it away, receiving serialized copies at the sandbox boundary. Our preferred deployment is OpenShell [47].
There are several promising directions to pursue:
Agent optimization via agent rewriting: First, agent optimization should move beyond prompt search toward rewriting every part of the agent: prompts, docstrings, typed method signatures, helper code, tool descriptions, context policies, retry loops, and decomposition structure. GEPA-style reflective optimization is a natural starting point [48], but the richer target is the whole agent object and its harness [48].
Skills as full software packages: Second, typed interfaces and libraries create a path toward self-evolving agents. Today's skills are often text snippets or informal procedures; we expect skills to become full software libraries with typed APIs, documentation, tests, examples, subagents, dependencies, and versioned interfaces that agents can inspect, call, repair, and extend.
Reinforcement learning to unlock inductive reasoning: Third, reinforcement learning can target the inductive reasoning needed by long-running agents. DeepSeek-R1 showed that outcome-driven reinforcement learning can induce useful reasoning behaviors when a model is allowed to search through intermediate reasoning steps [49]. We expect a similar effect for object-oriented agents, but over a richer action space than text alone. A $\textsc{NOOA}$ agent can choose what context to reveal, which variables to preserve, when to write deterministic helper code, when to promote a pattern into a reusable library, and when to decompose a task into deterministic orchestration. These are inductive decisions: the agent must generalize from prior trajectories to new tasks by identifying which abstractions, state variables, and decomposition strategies predict success. The hypothesis is that reinforcement learning over complete agent trajectories could teach models to use harness APIs, dynamic context, pass-by-reference objects, and code-as-interface as a learned reasoning substrate. In this view, the harness is not merely an execution environment; it is the action space in which agents learn to construct, test, and reuse problem-solving structure.
Taken together, these directions suggest that progress in agent capability will come not only from larger models or better prompts, but from the co-development of model and harness. We believe the software interface is the right place for that co-development. $\textsc{NOOA}$ is one step toward it: an object-oriented harness in which agents are programs that both humans and models can read, execute, test, and improve.
Appendix
Section Summary: The appendix supplies expanded technical comparisons of multiple agent frameworks and harnesses, verifying each against criteria such as typed inputs and outputs, live object references, executable code actions, loop engineering, object state handling, and exposed APIs, all tied to specific repository snapshots from July 2026 along with accompanying diagrams. It then presents a close-up examination of a demanding stress test involving sentiment classification of multiple texts, reproducing complete run traces that include the agent's Python code, system prompts, and colored message blocks showing context, execution outputs, and model responses across identical trials. These details allow readers to inspect how different systems manage orchestration, state, and complex tasks in practice.
A. Appendix: Harness comparison details
This appendix expands the compact comparison in Table 7. Each subsection begins with the pinned source snapshot (repository, commit, and package version, all retrieved on July 7, 2026) against which the scores were verified. Scores use the paper's model-visible rubrics: typed loop I/O requires typed inputs and outputs at the model-facing loop; pass-by-reference requires live object references rather than serialized text; code as action requires executable code with control flow and inline tool or method calls; loop engineering asks whether developers and models can program orchestration loops; object state asks whether the model can store and retrieve state through its working interface – Supported requires typed, model-visible state that is live within the session, so append-only memory text applied at the next session, and memory reachable only through dedicated tools, score Partial; and Harness APIs asks whether structured context blocks, per-turn dynamic context, and session events are exposed as model-visible APIs rather than hidden host machinery.
A.1 LangGraph / LangChain
A.2 LangChain Deep Agents
A.3 Microsoft Agent Framework
A.4 OpenAI Agents SDK
A.5 Google ADK
A.6 PydanticAI
A.7 smolagents
A.8 Claude Agent SDK
A.9 OpenAI Codex
A.10 OpenHands
A.11 PI
A.12 Hermes
A.13 OpenCode
A.14 OpenClaw
A.15 NOOA
B. Appendix: A stress test up close
This appendix shows four complete runs of sentiment_batch, the hardest capability stress test (31/50 overall). The listings are reproduced from the run traces: every ellipsis and truncation marker below was produced by the harness and seen by the model. Each message is a titled block whose colored left rule gives its role: amber for the cached system region, blue for user-role harness messages (task, execution output, dynamic context), and green for model output.
The test agent:
class SentimentBatchAgent(Agent):
"""You are an agent that classifies sentiment of multiple texts."""
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.method_writing = MethodWriting()
async def classify(
self, texts: Annotated[list[str], "The texts to classify"]
) -> list[Literal["positive", "negative", "neutral"]]:
"""Classify the sentiment of multiple texts."""
...
The scorer requires an exact match against 50 reference labels. All four runs received byte-identical context. The system region is shown first: the framework prompt, the CodeAct strategy instructions, the execution context, and the agent's own doc(self) rendering — the typed contract as the model sees it. Note the fan-out pattern and the instruction to return computed values by variable, both of which matter below.
<system_prompt expr="self._system_prompt()">
You are SentimentBatchAgent, a Python agent working in an interactive session.
## Context blocks
> **Section Summary**: The prompt organizes its information into sections marked by XML-style tags that wrap each block of content. Some of these blocks include a special attribute holding a Python expression that is automatically re-run and refreshed on every turn. Past system events are stored in tagged entries that can later be retrieved by a simple reference command.
Your prompt is organized in XML context blocks: `<name>CONTENT</name>`.
Blocks produced by `self.context.set_dynamic()` carry an `expr="..."` attribute whose value is the Python expression re-evaluated each turn.
Event history: system entries in `<sys tag="N">`; reference via `self.events["N"]`.
## Truncation
> **Section Summary**: In this system, short Python values such as literals are shown completely, while longer ones are abbreviated with markers that note the total length and display only the start and end sections. Class instances appear with their field names and values, using a trailing ellipsis when some fields are left out. The underlying data itself stays intact and can be accessed directly, but any omitted portions of captured text output cannot be recovered.
- A bare Python literal (`[1, 2, 3]`, `1: 2`, ''hello'`) is always complete.
- Truncated values use a `type(len=N, ...)` (or `type(repr_len=N, ...)`) marker:
list(len=100, [:5]=[...], [-5:]=[...])
tuple(len=100, [:5]=(...), [-5:]=(...))
dict(len=100, items={...})
set(len=100, items={...})
str(len=100000, [:250]='...', [-250:]='...')
ndarray(repr_len=233, [:100]='...', [-100:]='...')
- Structured instances (dataclasses, Pydantic, custom classes) render as `ClassName(field=value, ...)`; a trailing `...` means fields were elided:
Config(name='foo', enabled=True, ...)
- The variable itself is **not** truncated — index/iterate it directly to operate on the full data.
- `<truncated>...</truncated>` in captured stdout/stderr is **not recoverable**.
</system_prompt>
<strategy_prompt>
## Strategy
> **Section Summary**: This section describes an interactive, Jupyter-style Python environment in which preloaded parameters remain available as variables and session state carries forward between steps. Users can run code with direct support for await, print statements for debugging, and a doc helper to inspect objects, but they must invoke a tool on every turn rather than replying with plain text. The only available tools are execute_python to run code cells and return_result to submit a final answer, and failing to use them will abort the session.
Jupyter-like Python session. Parameters pre-loaded as locals; state persists across cells. Use `await` directly, `print`/`pprint` to debug, `doc(obj)` to inspect types. You MUST call a tool each turn — **plain-text responses do NOT end the session**. To finish, call `return_result(value)`. Repeated text-only responses will abort the run with an error.
**Your two tools:**
- `execute_python(code)` — run a code cell
- `return_result(value)` — submit your final answer (also callable from inside `execute_python`)
## When to use which tool
> **Section Summary**: Choose return_result for straightforward questions whose answers can be read straight from the given inputs, such as a simple yes/no or a single lookup. Turn to execute_python whenever the work involves lists, calculations, repeated steps, or data transformations, always performing those operations inside the code. For tasks that require understanding language—such as classification or interpretation—rely on the language model’s reasoning, either by answering directly or by calling a dedicated strategy function, rather than using pattern matching.
Use `return_result(...)` directly for simple answers determinable from the inputs alone (yes/no, one field, a single lookup).
Use `execute_python(...)` for lists/batches, arithmetic, multi-step computation, transforms, or iteration. Always iterate in code — never construct large arrays by hand.
For language tasks (classification, extraction, interpretation), use LLM reasoning — answer directly via `return_result`, or delegate to a `@strategy(PredictStrategy())` standalone function (see below). Don't keyword-match or regex.
## Returning computed results
After computing in code, call `return_result(variable)` **from within** `execute_python()`. This passes the variable directly. Do NOT re-type computed values in a separate `return_result` tool call.
## Helpers
Define helpers at the top of the cell and call them by name. Existing methods on `self` are usable via `await self.method(...)`. Helpers persist as REPL locals across cells in this session.
```python
def normalize(x):
return x.strip().lower()
cleaned = [normalize(v) for v in values]
```
## Fan-out generation
> **Section Summary**: To handle work on each item in a list, you mark an async function with a decorator that applies a chosen strategy and leave its body empty, then launch the calls together so they run at the same time. This pattern suits straightforward prediction tasks such as identifying the language of many messages. When a sub-task also needs to execute code, a different strategy is used, provided each new step stays simpler than the one that created it.
For per-item LLM work over a list, decorate a standalone async function with `@strategy(PredictStrategy())` and an ellipsis body. `asyncio.gather` runs the calls in parallel.
```python
@strategy(PredictStrategy())
async def detect_language(message: str) -> str:
"""Return the ISO 639-1 language code for message (e.g. 'en', 'fr', 'de', 'ja')."""
...
codes = await asyncio.gather(*(detect_language(m) for m in messages))
return_result(codes)
```
For iterative sub-tasks that need code execution, use `@strategy(CodeActStrategy())`. The sub-task must be strictly simpler than the current call to avoid infinite recursion.
## Restrictions (will throw)
> **Section Summary**: Certain built-in functions that allow running new code, inspecting internal variables, or managing asynchronous tasks are blocked and will raise errors if used. Attempts to attach new callable methods directly to the agent object are also forbidden. These measures prevent unsafe or uncontrolled behavior within the system.
- `eval`, `exec`, `compile`, `__import__`, `input`, `breakpoint`
- `globals`, `locals`, `vars`, `asyncio.run`, `loop.run_until_complete`
- Attaching callables to the agent: `self.foo = fn`, `setattr(self, 'foo', fn)`, `type(self).foo = fn`
</strategy_prompt>
<execution_context>
## Execution Context
> **Section Summary**: The execution context section describes the runtime environment and resources made available to an AI agent before it begins processing a sentiment classification task. This includes predefined agent classes such as SentimentBatchAgent, imported libraries and strategies for task decomposition, and built-in functions like print, doc, and return_result that the agent can always access. It also shows how the system supplies the agent with the task description, inspects input variables such as a list of 50 texts, and shares the current agent state so the model has full context before writing any code.
**Available types** (defined in agent or ancestor modules): SentimentBatchAgent
Tip: Use `doc(SentimentBatchAgent)` to inspect fields before constructing
**Imported items**: Agent, Annotated, Literal, MethodWriting
**Task decomposition**: `@strategy(PredictStrategy())` decorator, `strategy`, `PredictStrategy`, `CodeActStrategy`
**Stdlib**: `asyncio`, `typing` (Literal, Annotated, etc.)
**Always available**: `self`, `print()`, `pprint()`, `doc()`, `return_result()`, `reasoning()` method parameters
</execution_context>
<self expr="doc(type(self))">
class SentimentBatchAgent:
"""You are an agent that classifies sentiment of multiple texts."""
method_writing: MethodWriting = MethodWriting() # Define helpers and LLM-powered sub-calls at the top of a REPL cell.
async def classify(self, texts: list[str]) -> list[Literal[positive, negative, neutral]]:
"""
Classify the sentiment of multiple texts.
Args:
texts: The texts to classify
"""
</self>
Each run then begins with the task, the harness-initiated input inspection and its output, and the per-turn state block:
<sys tag="1">
Task(prompt='''## Task: classify
Classify the sentiment of multiple texts.
You are executing `classify` — code runs in the Execution Context above. Calling `self.classify(...)` would recurse.''')
</sys>
<sys tag="3">
PythonOutput(tool_call_id='prefill_8b0f55d4', execution_status=<ResultStatus.COMPLETE: 'complete'>, stdout='''Task: classify()
texts (list):
list(len=50,
[:13]=[
'This is the best day ever!',
"I'm so disappointed with the service.",
'The temperature is 72 degrees.',
'What a fantastic experience!',
'I regret buying this.',
'The meeting is at 3pm.',
"I'm thrilled with these results!",
'Terrible quality, waste of money.',
'The report contains 50 pages.',
"I couldn't be happier with the result!",
'Absolutely love it!',
'This is awful and unusable.',
"It's okay, nothing special.",
],
[-12:]=[
'Perfect in every way!',
'This failed miserably.',
'The document is 10 pages long.',
'Amazing results, so pleased!',
'Very poor quality, extremely disappointed.',
'Typical response time.',
'Fantastic product, love it!',
'This is a complete mess.',
'The office temperature is 75°F.',
'Absolutely thrilled with this purchase!',
'Deeply unsatisfied, very frustrating.',
'Regular maintenance is required.',
],
)
''')
</sys>
The model sees the type, the true length (len=50), and 25 of the 50 texts; the variable texts holds all 50. The four runs diverge at the first model-authored cell.
B.1 Nemotron 3 Ultra — passed
The intended solution in one model-authored cell: a model-defined subagent, fanned out over the live variable, with the live result returned from inside the cell. 9.6 seconds end to end.
@strategy(PredictStrategy())
async def classify_text(text: str) -> str:
"""Classify the sentiment of this text as 'positive', 'negative', or 'neutral'.
Text: text
"""
...
# Process all texts in parallel
results = await asyncio.gather(*(classify_text(text) for text in texts))
pprint(results)
return_result(results)return_result(results)
B.2 Claude Opus 4.8 — failed
The model's first cell: the same fan-out, executed correctly.
@strategy(PredictStrategy())
async def classify_sentiment(text: str) -> Literal["positive", "negative", "neutral"]:
"""Classify the sentiment of text as 'positive', 'negative', or 'neutral'."""
...
results = await asyncio.gather(*(classify_sentiment(t) for t in texts))
for t, r in zip(texts, results):
print(f"r:8 | t")print(f"r:8 | t")
positive | This is the best day ever!
negative | I'm so disappointed with the service.
neutral | The temperature is 72 degrees.
positive | What a fantastic experience!
negative | I regret buying this.
neutral | The meeting is at 3pm.
positive | I'm thrilled with these results!
negative | Terrible quality, waste of money.
neutral | The report contains 50 pages.
positive | I couldn't be happier with the result!
positive | Absolutely love it!
negative | This is awful and unusable.
neutral | It's okay, nothing special.
positive | Outstanding performance!
negative | What a disaster this turned out to be.
neutral | The documentation is available online.
positive | Exceeded all my expectations!
negative | Completely unacceptable service.
neutral | The package weighs 5 pounds.
negative | Never buying from them again.
positive | Phenomenal work, truly impressive!
negative | This is garbage and doesn't work.
neutral | The file size is 2MB.
positive | Incredible experience, highly satisfied!
negative | Worst purchase I've ever made.
neutral | Standard shipping procedure.
positive | Highly recommend this to everyone!
negative | Total waste of time and money.
neutral | The price is \$49.99.
positive | Love every aspect of this product!
negative | Disappointing quality, very unhappy.
neutral | Average performance, nothing notable.
positive | Brilliant solution to the problem!
negative | This is useless and broken.
neutral | The meeting starts at noon.
positive | Exceeded all expectations, amazing!
negative | Completely frustrated with this.
neutral | Normal business operations.
positive | Perfect in every way!
negative | This failed miserably.
neutral | The document is 10 pages long.
positive | Amazing results, so pleased!
negative | Very poor quality, extremely disappointed.
neutral | Typical response time.
positive | Fantastic product, love it!
negative | This is a complete mess.
neutral | The office temperature is 75°F.
positive | Absolutely thrilled with this purchase!
negative | Deeply unsatisfied, very frustrating.
neutral | Regular maintenance is required.
On the next turn, instead of return_result(results), the model transcribed the printed output into a literal in a separate return_result tool call --- exactly what the strategy instructions above say not to do. The transcription dropped item 43 (neutral for "Typical response time."):
Verdict: List length mismatch: expected 50, got 49. The live results variable held all 50 labels.
B.3 GPT-5.5 — passed
No subagents. The model's first cell defeats the preview deliberately, printing every item with its index:
for i,t in enumerate(texts):
print(i, repr(t))
0 'This is the best day ever!'
1 "I'm so disappointed with the service."
2 'The temperature is 72 degrees.'
3 'What a fantastic experience!'
4 'I regret buying this.'
5 'The meeting is at 3pm.'
6 "I'm thrilled with these results!"
7 'Terrible quality, waste of money.'
8 'The report contains 50 pages.'
9 "I couldn't be happier with the result!"
10 'Absolutely love it!'
11 'This is awful and unusable.'
12 "It's okay, nothing special."
13 'Outstanding performance!'
14 'What a disaster this turned out to be.'
15 'The documentation is available online.'
16 'Exceeded all my expectations!'
17 'Completely unacceptable service.'
18 'The package weighs 5 pounds.'
19 'Never buying from them again.'
20 'Phenomenal work, truly impressive!'
21 "This is garbage and doesn't work."
22 'The file size is 2MB.'
23 'Incredible experience, highly satisfied!'
24 "Worst purchase I've ever made."
25 'Standard shipping procedure.'
26 'Highly recommend this to everyone!'
27 'Total waste of time and money.'
28 'The price is \$49.99.'
29 'Love every aspect of this product!'
30 'Disappointing quality, very unhappy.'
31 'Average performance, nothing notable.'
32 'Brilliant solution to the problem!'
33 'This is useless and broken.'
34 'The meeting starts at noon.'
35 'Exceeded all expectations, amazing!'
36 'Completely frustrated with this.'
37 'Normal business operations.'
38 'Perfect in every way!'
39 'This failed miserably.'
40 'The document is 10 pages long.'
41 'Amazing results, so pleased!'
42 'Very poor quality, extremely disappointed.'
43 'Typical response time.'
44 'Fantastic product, love it!'
45 'This is a complete mess.'
46 'The office temperature is 75°F.'
47 'Absolutely thrilled with this purchase!'
48 'Deeply unsatisfied, very frustrating.'
49 'Regular maintenance is required.'
The model's second cell labels by hand — transcription again, but with explicit per-item bookkeeping:
The model's only cell: a keyword-rule classifier, with keyword lists fitted to the 25 texts visible in the preview, applied blind to all 50.
labels = []
labels = []
for t in texts:
s = t.lower()
pos = any(w in s for w in ['best','fantastic','thrilled','happier','love','perfect','amazing','pleased','thrilled','excellent','great'])
neg = any(w in s for w in ['disappointed','regret','terrible','awful','poor','failed','waste','unusable','miserably','unsatisfied','mess','frustrating'])
if pos and not neg:
labels.append('positive')
elif neg and not pos:
labels.append('negative')
elif pos and neg:
labels.append('neutral')
else:
labels.append('neutral')
return_result(labels)
It iterates the live variable correctly, but substitutes keyword rules for semantic judgment — against the strategy instructions — on 25 texts it never inspected; the labels do not match.
B.5 What the four runs show
Sophistication and success are orthogonal: the most advanced harness use (Opus's fan-out) failed on the cheapest discipline — return the variable, do not retype it — while the least agentic approach (GPT-5.5's manual labeling) passed on careful bookkeeping. Both failures ignored an explicit instruction in the strategy prompt, and both had a safe path already provided by the interface. This is the pattern behind the stress-test results in Section 4: the remaining failures are not gaps in interface understanding but lapses in disciplined use of it — and they are exactly the behaviors that trajectory-level reinforcement learning (Section 7) could target.
C. Appendix: Memory-System Details
C.1 Design decisions
The subsystem is additive by construction: in linecodeMemoryManager.install(agent) wires storage, retrieval, and hooks onto an unmodified agent through existing extension points (event subscriptions, call middleware, context blocks), and uninstalling restores the agent exactly. Four decisions are load-bearing. (i) Verbal boundary: tools accept and render verbal descriptors ($\textsc{critical} \ldots \textsc{trivial}$; $\textsc{open}$ / $\textsc{done}$ / $\textsc{dropped}$) while scoring stays numeric internally, keeping the model-facing vocabulary in-distribution. (ii) Injection never self-reinforces: spontaneous recall runs the same retrieval pipeline with in linecodetouch=False, so what the harness chooses to show does not inflate ACT-R activation; only deliberate tool recall does. (iii) One SQLite file as source of truth: records, a typed memory graph, maintenance log, and per-memory access records live in a single human-inspectable file; vector indexes (numpy, sqlite-vec, or Chroma) are derived and rebuilt on demand. (iv) Pass-by-reference memories: a record may hold in linecodekind:key references resolved against live agent state at recall time by strict name lookup (never in linecodeeval), returning a $\textsc{live}$ value or an explicitly $\textsc{dangling}$ snapshot -- eliminating the stale-copy failure mode we measured with copied values. Prospective state is first-class: todo memories carry a lifecycle, survive pruning while open, and can be surfaced each turn. Together, the tools and the reflection pipeline carry skill-library and self-critique memory [50, 51] into the object model. Observability is self-contained: every access is recorded on the memory itself, a retrieval call can be replayed with in linecodeexplain(), and memory events bridge to OpenTelemetry spans with trace $\leftrightarrow$ record cross-links. The controlled measurement of the subsystem's effect is the ARC-AGI-3 ablation (Section 4.5): +11.8 RHAE points over the identical agent with file-based notes in place of memory. In small internal pilots, reflection helped when retrieval was the bottleneck and hurt pinpoint lookup (abstraction blurs the exact fact), which is why consolidation is configurable per store.
C.2 Memory across today's harnesses
Three families dominate current systems. Flat markdown, always in context (Claude Code's CLAUDE.md, Codex's AGENTS.md, Gemini CLI's GEMINI.md, Cursor rules): human-authored, transparent, versionable – but token cost grows linearly and nothing is learned automatically. Vector stores, similarity-retrieved (AutoGen teachability, CrewAI, Mem0-style layers, Letta archival): automatic accumulation at unbounded scale – but opaque to the user and unverified at write time. Structured self-edited context (Letta memory blocks, LangMem managed memories): typed segments the agent maintains, occasionally consolidated in the background. During 2025–2026 the CLI harnesses converged on a two-layer hybrid – a human instruction file plus a model-written auto-memory layer – differing mainly in whether the auto layer is user-readable and whether retrieval is bounded. The $\textsc{NOOA}$ memory system sits at the intersection of the families: file-based and human-auditable like the first, automatically written like the auto-memory layers, and typed, scored, and graph-linked like the structured family, with cognitively grounded retrieval (ACT-R activation, Ebbinghaus decay) in place of plain similarity search. Table 8 summarizes.
::: {caption="Table 8: Memory subsystems of agent harnesses and frameworks, July 2026."}
:::
D. Appendix: ARC-AGI-3 Example Details
D.1 From DreamTeam to one agent and one skill
Table 9 maps each element of the DreamTeam system [10] onto the $\textsc{NOOA}$ example. The methodology is kept intact – latent encoding under a declared schema, executable dynamics, retrodiction as the sole refinement signal, search over the learned model, level-boundary reflection with carry-forward – while the apparatus (roles, inter-agent protocol, harness-side evaluation engine, background search workers) is either absorbed by framework primitives or performed by the agent itself in its REPL. The paper system is $\sim$ 150k lines with 1,821 lines of role prompts; the example is $\sim$ 6.1k lines with a 50-line skill.
\begin{tabular}{p{0.18\linewidth}p{0.40\linewidth}p{0.36\linewidth}}
\toprule
\textbf{Element} & \textbf{DreamTeam (paper system)} & \textbf{\textsc{NOOA} example} \\
\midrule
Encode $\to$ latent $z$ & Observer role (198-line prompt); schema-validated \texttt{observable.py}; per-step encoding log & skill item: \texttt{encode(grid) $\to$ z} with declared \texttt{Z\_SCHEMA}, persisted as a helper module \\
Predict dynamics & Simulator role; \texttt{predict()} + recurrent \texttt{history()} with per-hypothesis branches & single \texttt{predict(z, a) $\to$ z'} \\
Retrodiction & harness-computed each step by a 4{,}690-line evaluation engine (DreamerV3 pattern) & agent-performed in the REPL each turn \\
Search / planning & Explorer role; sub-goal/policy registries; background search worker process & BFS/greedy/best-first over own \texttt{predict} inside CodeAct cells \\
Verification & Critic + team-leader roles; per-action approve/reject; feedback protocol & turn contract: \texttt{submit\_actions(..., rationale=``predict: ...'')} checked next turn \\
Memory across levels & per-role step/level logs with carry-forward ledgers; harness-forced reflection & memory subsystem (Section 3.7): recall before deciding, reflect at level boundaries and before \texttt{RESET} \\
Team communication & feedback protocol, file registry, shared workspace & none -- one agent, one workspace \\
\bottomrule
\end{tabular}
D.2 Containment and red-team audit
The threat model has three rules: no internet, no access to a game's generating source or identity, no access to other runs, other games, or prior solutions. Defenses are layered and – critically – the hard layers are external to the agent, so reading the defense code does not help escape: (i) per cell, an in-process AST cell guard, module denylist, and in linecodeopen() jail, backed by an opt-in per-cell OS sandbox that forks each CodeAct cell into a locked-down worker with four kernel-enforced, irrevocable guards installed before any cell bytecode runs -- Landlock filesystem default-deny, a seccomp block on socket(AF_INET/AF_INET6), RLIMIT_AS/RLIMIT_CPU memory/CPU caps, and a hard SIGTERM $\to$ SIGKILL cell timeout; (ii) per run, an OS-level uid drop (setpriv --no-new-privs; game source readable only by root; sibling runs carved out), with a fail-closed namespace sandbox (bubblewrap) as the current default; (iii) end-to-end anonymisation -- agents only ever see an opaque game-<hex> alias, with redaction on outputs and state.
A red-team loop re-scanned the live 25-game fleet every 30 minutes – 18 passes – classifying the code inside every executed cell and the data returned by every tool result. Results: no leakage on any rule. Zero network invocations; zero game-source bytes returned; cross-game reads fail with EACCES and the per-game memory stores remained fully isolated; a name-leak scanner found zero occurrences of any real game identifier across 13,335 agent logs (aliases only). There was exactly one escape attempt across the fleet: one agent invoked a filesystem-recon shell command (find/grep for its own store alias); the cell guard replaced the cell before execution and zero bytes were returned. One latent finding (harness log files world-readable inside the run directory) was never referenced by any agent; the one-line fix is documented. A companion analysis documents a known cell-guard gap (dynamic attribute lookup can evade the AST scan) together with its backstop: under the uid drop, even a reached shell cannot read the game source. No game used the gap.
D.3 World-model usage evidence and failure modes
Of 25 games, 22 persisted executable model code (37 modules, $\sim$ 4.4k lines); six games grew a new per-level module as mechanics accumulated (hazards $\to$ tokens $\to$ doors $\to$ pressure plates). By deepest observed use: 5 games ran the full loop (predict + search + retrodiction), 7 planned or predicted with their models, 10 used them for perception/encoding only. Representative closed loops: m0r0 stored a 42-action plan, replayed twenty real frames through in linecodeencode to check it mid-execution ("matched the model exactly"), released the next batch, and pre-announced the completing action of its final level -- 6/6 levels near the per-level score cap; tu93 passed its planner's output verbatim to in linecodesubmit_actions with the prediction in the rationale; ar25 wrote its model on turn one from a single exploratory action, then submitted a 16-action plan ending "expect level completion on the last DOWN" – 8/8 levels in 24 turns. Model depth tracked what each game demanded rather than raw level count; its payoff shows up as action efficiency (near-cap per-level scores, long verified batches).
The failure mode is instructive: the two games that hung did so in ad-hoc, in-cell searches that lacked the bounds (max_depth, visited sets, node budgets) their own persisted planners carried -- one branched over all 3,456 click targets per node with no budget while its persisted predict went uncalled. Durable, curated artifacts were reliably better engineered than improvised cell code, which argues for the memory-and-workspace discipline of Section 3.7 and for hard cell timeouts in the harness, now provided by the per-cell OS sandbox above.
D.4 Memory-system usage during play
We instrumented all three interfaces of the memory subsystem (Section 3.7) across the 25 per-game stores: writes (agent tools plus consolidation-created records), spontaneous reads (the in linecodeBeforeTurn injection into the dynamic context block), and deliberate reads (the in linecoderecall/ in linecodesearch tools). Ground truth comes from the SQLite stores themselves – each record carries uncapped per-channel counters – with event-level statistics (injections per turn, hit rates) from the OTel trace exports. Figure 9 shows the type and importance distributions per interface; Table 6 gives the counts.
Five observations. (i) The channels select differently: mean importance climbs written $\to$ injected $\to$ deliberate (6.1 $\to$ 7.2 $\to$ 7.5), and the $\textsc{high}$ verbal level carries 61% of writes but 87% of injected and 91% of deliberate occurrences – the ACT-R importance term biases both read channels toward what the agent itself marked important. (ii) Injection is selective and bounded: only 632 of 3,262 memories (19%) ever surfaced spontaneously, at 4.1 memories $\approx$ 1.9k characters per turn – the char-budgeted block prevents context flooding by memory. (iii) Episodes are the recency channel: 10% of writes but 24% of injected occurrences (13% deliberate) – the base-level recency term surfaces the latest level attempts unprompted, while deliberate recall goes after facts (info: 82% of tool-read occurrences at a 99--100% hit rate, 9.7 results per call). (iv) Skills are few, dear, and deliberately fetched: 3% of writes but the highest importance of any type (8.3) and over-represented in deliberate reads -- agents went back for their verified procedures. (v) Consolidation compressed the store rather than growing it: reflection records are 22% of rows yet $\sim$ 1% of both read channels (importance 3.9), and 45% of all records ended archived by decay-based forgetting. The intent and scratch types went unused; todo appeared in 18 records. Per-game store sizes ranged 23/129/255 (min/median/max).
Memory engagement per decision tracks performance. Because raw store volume largely reflects run length (longer games accumulate more turns, and every turn leaves memory behind), the informative measure is memory use per decision – one decision being one agent turn ending in in linecodesubmit_actions. On this measure the relationship with performance is clearly positive (Figure 8): deliberate recalls per decision correlate with levels completed at Spearman $\rho=+0.52$, and writes per decision at $\rho=+0.36$. Winning games check memory 1.63 times and write 1.87 memories per decision (medians, vs. 1.21 and 1.46 for the remaining games), and every winning game makes at least one deliberate recall per decision – the skill's recall-before-deciding discipline in action. Spontaneous injection is cadence-fixed at $\approx$ 1 per turn by design and therefore uniform across the fleet. With $n=25$ and 16 outcomes right-censored by the operator kill, these are associations.
D.5 Reproduction
Runs analyzed: the RHAE curves and 2-hour numbers are the guarded, cache-aware fleets 2026071 $6_204102_c$ ompetition_gpt5 $5_g$ uarded (GPT-5.5) and 2026071 $8_012940_c$ ompetition_gpt56sol_guarded (GPT-5.6-sol), with 2026071 $0_154254_c$ ompetition_memory_visual (baseline) and 2026071 $4_201702_c$ ompetition_md (markdown-file ablation) for reference; all 25 games each, and they regenerate from the per-game event logs via tmp/nooa_paper_contribution/artifacts/performance_2h.py. The memory-usage analysis is from 2026071 $1_193827_c$ ompetition_memory_visual_wm (world-model skill, 25 games, GPT-5.5) via memory_usage_analysis.py in the same directory. Pricing $5/$30/$0.50 per Mtok (input/output/cached).
References
Section Summary: This section provides a bibliography of sources on AI agent development, frameworks, and evaluation methods. It cites popular tools like LangChain and LlamaIndex along with Anthropic's guides on building effective agents, plus academic papers on memory systems, generative agents, and code-based reasoning. The list also includes benchmarks for real-world tasks such as software engineering and cybersecurity, along with related open-source projects and libraries.
[6] Adam Paszke et al. (2019). PyTorch: An imperative style, high-performance deep learning library. In Advances in Neural Information Processing Systems (NeurIPS). https://arxiv.org/abs/1912.01703.
[7] Charles Packer et al. (2023). MemGPT: Towards LLMs as operating systems. arXiv preprint arXiv:2310.08560. https://arxiv.org/abs/2310.08560.
[12] Joon Sung Park et al. (2023). Generative Agents: Interactive Simulacra of Human Behavior. In Proceedings of the 36th Annual ACM Symposium on User Interface Software and Technology (UIST). https://arxiv.org/abs/2304.03442.
[13] Carlos E. Jimenez et al. (2024). SWE-bench: Can language models resolve real-world GitHub issues?. In The Twelfth International Conference on Learning Representations. https://openreview.net/forum?id=VTF8yNQM66.
[14] Mike A. Merrill et al. (2026). Terminal-Bench: Benchmarking Agents on Hard, Realistic Tasks in Command Line Interfaces. arXiv preprint arXiv:2601.11868. https://arxiv.org/abs/2601.11868.
[15] Zhun Wang et al. (2026). CyberGym: Evaluating AI Agents' Real-World Cybersecurity Capabilities at Scale. In The Fourteenth International Conference on Learning Representations. https://openreview.net/forum?id=2YvbLQEdYt.
[19] Xingyao Wang et al. (2024). OpenHands: An open platform for AI software developers as generalist agents. arXiv preprint arXiv:2407.16741. https://arxiv.org/abs/2407.16741.
[20] Omar Khattab et al. (2024). DSPy: Compiling Declarative Language Model Calls into Self-Improving Pipelines. In International Conference on Learning Representations (ICLR). https://arxiv.org/abs/2310.03714.
[21] Luca Beurer-Kellner et al. (2023). Prompting is programming: A query language for large language models. In PLDI. https://arxiv.org/abs/2212.06094.
[22] Brandon T. Willard and Rémi Louf (2023). Efficient guided generation for large language models. arXiv preprint arXiv:2307.09702. https://arxiv.org/abs/2307.09702.
[27] Luyu Gao et al. (2022). PAL: Program-aided language models. arXiv preprint arXiv:2211.10435. https://arxiv.org/abs/2211.10435.
[28] Wenhu Chen et al. (2022). Program of thoughts prompting: Disentangling computation from reasoning for numerical reasoning tasks. arXiv preprint arXiv:2211.12588. https://arxiv.org/abs/2211.12588.
[29] Chengshu Li et al. (2023). Chain of code: Reasoning with a language model-augmented code emulator. arXiv preprint arXiv:2312.04474. https://arxiv.org/abs/2312.04474.
[30] Xingyao Wang et al. (2024). Executable code actions elicit better LLM agents. arXiv preprint arXiv:2402.01030. https://arxiv.org/abs/2402.01030.
[38] Ellie Y. Cheng et al. (2026). Sharing State Between Prompts and Programs. In International Conference on Learning Representations (ICLR). https://arxiv.org/abs/2512.14805.
[39] Katsumi Okuda and Saman Amarasinghe (2024). AskIt: Unified Programming Interface for Programming with Large Language Models. In Proceedings of the 2024 IEEE/ACM International Symposium on Code Generation and Optimization (CGO). https://arxiv.org/abs/2308.15645.
[40] Di Huang et al. (2023). ANPL: Towards Natural Programming with Interactive Decomposition. In Advances in Neural Information Processing Systems (NeurIPS). https://arxiv.org/abs/2305.18498.
[41] Bo Qiao et al. (2024). TaskWeaver: A Code-First Agent Framework. arXiv preprint arXiv:2311.17541. https://arxiv.org/abs/2311.17541.
[46] Sikuan Yan et al. (2025). Memory-R1: Enhancing large language model agents to manage and utilize memories via reinforcement learning. arXiv preprint arXiv:2508.19828. https://arxiv.org/abs/2508.19828.
[48] Lakshya A. Agrawal et al. (2026). GEPA: Reflective prompt evolution can outperform reinforcement learning. In ICLR. https://arxiv.org/abs/2507.19457.
[49] DeepSeek-AI (2025). DeepSeek-R1: Incentivizing reasoning capability in LLMs via reinforcement learning. arXiv preprint arXiv:2501.12948. https://arxiv.org/abs/2501.12948.
[50] Guanzhi Wang et al. (2024). Voyager: An Open-Ended Embodied Agent with Large Language Models. Transactions on Machine Learning Research. https://arxiv.org/abs/2305.16291.
[51] Noah Shinn et al. (2023). Reflexion: Language Agents with Verbal Reinforcement Learning. In Advances in Neural Information Processing Systems (NeurIPS). https://arxiv.org/abs/2303.11366.