API Reference for rune-agent

rune_agent

Rune coding agent -- recursive code generation with parametric memory.

Classes

RuneState

Bases: TypedDict

State for the Rune coding agent recursive loop.

Attributes:

Name Type Description
task_description str

Natural language description of the coding task.

task_type str

Category of task (e.g. 'function', 'class', 'refactor').

test_suite str

Test code that the generated solution must pass.

adapter_ids list[str]

LoRA adapter IDs to load for parametric memory.

session_id str

Unique identifier for trajectory persistence (UUID4).

attempt_count int

Current attempt number (0-indexed, incremented by reflect).

max_attempts int

Maximum number of generation attempts allowed.

generated_code str

Code produced by the generate node.

stdout str

Standard output from executing generated code.

stderr str

Standard error from executing generated code.

exit_code int

Process exit code from execution (0 = success).

tests_passed bool

Whether the test suite passed on this attempt.

test_count int

Number of unittest tests that ran.

tests_ran bool

Whether any unittest tests actually executed.

trajectory list[dict[str, Any]]

List of per-attempt records for parametric memory training.

outcome Optional[str]

Terminal result -- 'success', 'exhausted', or None if still running.

Functions

create_graph

create_graph() -> Any

Create and compile the Rune agent workflow graph.

The graph follows this flow: START -> generate -> execute -> reflect -> [should_retry] -> generate (retry) OR save_trajectory -> END

Returns:

Type Description
Any

Compiled StateGraph ready for execution.

Example

graph = create_graph() graph is not None True

Source code in services/rune-agent/src/rune_agent/graph.py
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
def create_graph() -> Any:
    """Create and compile the Rune agent workflow graph.

    The graph follows this flow:
    START -> generate -> execute -> reflect -> [should_retry]
        -> generate (retry) OR save_trajectory -> END

    Returns:
        Compiled StateGraph ready for execution.

    Example:
        >>> graph = create_graph()
        >>> graph is not None
        True
    """
    workflow = StateGraph(RuneState)

    workflow.add_node("generate", generate_node)
    workflow.add_node("execute", execute_node)
    workflow.add_node("reflect", reflect_node)
    workflow.add_node("save_trajectory", save_trajectory_node)

    workflow.add_edge(START, "generate")
    workflow.add_edge("generate", "execute")
    workflow.add_edge("execute", "reflect")
    workflow.add_conditional_edges("reflect", should_retry)
    workflow.add_edge("save_trajectory", END)

    return workflow.compile()

get_graph

get_graph() -> Any

Get or create the compiled graph instance (thread-safe singleton).

Uses double-checked locking so the expensive create_graph() call is only made once, even if multiple threads call get_graph() concurrently.

Returns:

Type Description
Any

Compiled StateGraph instance.

Source code in services/rune-agent/src/rune_agent/graph.py
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
def get_graph() -> Any:
    """Get or create the compiled graph instance (thread-safe singleton).

    Uses double-checked locking so the expensive create_graph() call is only
    made once, even if multiple threads call get_graph() concurrently.

    Returns:
        Compiled StateGraph instance.
    """
    global _graph
    if _graph is None:
        with _graph_lock:
            if _graph is None:
                _graph = create_graph()
    return _graph

Modules

graph

LangGraph workflow for the Rune coding agent recursive loop.

Supports two modes: 1. Standard loop (create_graph): generate → execute → reflect → retry/save 2. Single iteration (create_single_iteration_graph): generate → execute → reflect → END. Used by the outer rune_runner iteration loop where the hypernetwork produces a new adapter between iterations.

Classes
Functions
should_retry
should_retry(
    state: RuneState,
) -> Literal["generate", "save_trajectory"]

Route from reflect: retry if attempts remain and tests failed, else save.

This is a REAL implementation, not a stub: - If tests passed -> save_trajectory (success) - If attempt_count >= max_attempts -> save_trajectory (exhausted) - Otherwise -> generate (retry)

Parameters:

Name Type Description Default
state RuneState

Current agent state after reflection.

required

Returns:

Type Description
Literal['generate', 'save_trajectory']

Next node name: 'generate' to retry or 'save_trajectory' to finish.

Example

state = {"tests_passed": False, "attempt_count": 0, "max_attempts": 3} should_retry(state) 'generate' state2 = {"tests_passed": True, "attempt_count": 1, "max_attempts": 3} should_retry(state2) 'save_trajectory'

Source code in services/rune-agent/src/rune_agent/graph.py
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
def should_retry(state: RuneState) -> Literal["generate", "save_trajectory"]:
    """Route from reflect: retry if attempts remain and tests failed, else save.

    This is a REAL implementation, not a stub:
    - If tests passed -> save_trajectory (success)
    - If attempt_count >= max_attempts -> save_trajectory (exhausted)
    - Otherwise -> generate (retry)

    Args:
        state: Current agent state after reflection.

    Returns:
        Next node name: 'generate' to retry or 'save_trajectory' to finish.

    Example:
        >>> state = {"tests_passed": False, "attempt_count": 0, "max_attempts": 3}
        >>> should_retry(state)
        'generate'
        >>> state2 = {"tests_passed": True, "attempt_count": 1, "max_attempts": 3}
        >>> should_retry(state2)
        'save_trajectory'
    """
    if state["tests_passed"]:
        return "save_trajectory"
    if state["attempt_count"] >= state["max_attempts"]:
        return "save_trajectory"
    return "generate"
create_graph
create_graph() -> Any

Create and compile the Rune agent workflow graph.

The graph follows this flow: START -> generate -> execute -> reflect -> [should_retry] -> generate (retry) OR save_trajectory -> END

Returns:

Type Description
Any

Compiled StateGraph ready for execution.

Example

graph = create_graph() graph is not None True

Source code in services/rune-agent/src/rune_agent/graph.py
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
def create_graph() -> Any:
    """Create and compile the Rune agent workflow graph.

    The graph follows this flow:
    START -> generate -> execute -> reflect -> [should_retry]
        -> generate (retry) OR save_trajectory -> END

    Returns:
        Compiled StateGraph ready for execution.

    Example:
        >>> graph = create_graph()
        >>> graph is not None
        True
    """
    workflow = StateGraph(RuneState)

    workflow.add_node("generate", generate_node)
    workflow.add_node("execute", execute_node)
    workflow.add_node("reflect", reflect_node)
    workflow.add_node("save_trajectory", save_trajectory_node)

    workflow.add_edge(START, "generate")
    workflow.add_edge("generate", "execute")
    workflow.add_edge("execute", "reflect")
    workflow.add_conditional_edges("reflect", should_retry)
    workflow.add_edge("save_trajectory", END)

    return workflow.compile()
create_single_iteration_graph
create_single_iteration_graph() -> Any

Create a graph for one iteration: generate → execute → reflect → END.

Used by rune_runner's outer iteration loop. Each iteration runs one generate/execute/reflect cycle, then returns control to the outer loop which runs the hypernetwork to produce a new adapter for the next iteration.

Returns:

Type Description
Any

Compiled StateGraph for single-iteration execution.

Source code in services/rune-agent/src/rune_agent/graph.py
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
def create_single_iteration_graph() -> Any:
    """Create a graph for one iteration: generate → execute → reflect → END.

    Used by rune_runner's outer iteration loop. Each iteration runs one
    generate/execute/reflect cycle, then returns control to the outer loop
    which runs the hypernetwork to produce a new adapter for the next iteration.

    Returns:
        Compiled StateGraph for single-iteration execution.
    """
    workflow = StateGraph(RuneState)

    workflow.add_node("generate", generate_node)
    workflow.add_node("execute", execute_node)
    workflow.add_node("reflect", reflect_node)

    workflow.add_edge(START, "generate")
    workflow.add_edge("generate", "execute")
    workflow.add_edge("execute", "reflect")
    workflow.add_edge("reflect", END)

    return workflow.compile()
get_graph
get_graph() -> Any

Get or create the compiled graph instance (thread-safe singleton).

Uses double-checked locking so the expensive create_graph() call is only made once, even if multiple threads call get_graph() concurrently.

Returns:

Type Description
Any

Compiled StateGraph instance.

Source code in services/rune-agent/src/rune_agent/graph.py
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
def get_graph() -> Any:
    """Get or create the compiled graph instance (thread-safe singleton).

    Uses double-checked locking so the expensive create_graph() call is only
    made once, even if multiple threads call get_graph() concurrently.

    Returns:
        Compiled StateGraph instance.
    """
    global _graph
    if _graph is None:
        with _graph_lock:
            if _graph is None:
                _graph = create_graph()
    return _graph

nodes

Node functions for the Rune coding agent recursive loop.

Classes
Functions
generate_node async
generate_node(state: RuneState) -> dict[str, Any]

Generate code for the given task using the inference layer.

Calls the LLM (with optional LoRA adapters) to produce a code solution for the task description.

Parameters:

Name Type Description Default
state RuneState

Current agent state with task description and context.

required

Returns:

Type Description
dict[str, Any]

State update dict with generated_code key.

Example

state = {"task_description": "Write fibonacci", "task_type": "function", ... "test_suite": "assert fib(5) == 5", "adapter_ids": []} result = await generate_node(state) 'generated_code' in result True

Source code in services/rune-agent/src/rune_agent/nodes.py
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
async def generate_node(state: RuneState) -> dict[str, Any]:
    """Generate code for the given task using the inference layer.

    Calls the LLM (with optional LoRA adapters) to produce a code solution
    for the task description.

    Args:
        state: Current agent state with task description and context.

    Returns:
        State update dict with generated_code key.

    Example:
        >>> state = {"task_description": "Write fibonacci", "task_type": "function",
        ...          "test_suite": "assert fib(5) == 5", "adapter_ids": []}
        >>> result = await generate_node(state)
        >>> 'generated_code' in result
        True
    """
    # Read env vars inside function body so monkeypatch.setenv() works in tests
    model: str = os.environ.get("RUNE_MODEL", DEFAULT_MODEL)
    adapter_id: str | None = state["adapter_ids"][0] if state["adapter_ids"] else None

    provider = get_provider()
    user_prompt = _build_prompt(state)
    phase = state.get("phase")
    system_prompt = _PHASE_SYSTEM_PROMPTS.get(phase or "", DEFAULT_SYSTEM_PROMPT)

    max_tokens = int(os.environ.get("RUNE_MAX_TOKENS", "1024"))

    result: GenerationResult = await provider.generate(
        prompt=user_prompt,
        model=model,
        adapter_id=adapter_id,
        max_tokens=max_tokens,
        system_prompt=system_prompt,
    )

    extracted = _extract_code(result.text)
    logger.info(
        "generate_node: attempt=%d, model=%s, adapter_id=%s, tokens=%d, finish=%s",
        state["attempt_count"],
        result.model,
        result.adapter_id,
        result.token_count,
        result.finish_reason,
    )

    return {"generated_code": extracted, "finish_reason": result.finish_reason}
execute_node async
execute_node(state: RuneState) -> dict[str, Any]

Execute the generated code in a sandboxed environment.

Runs the generated code against the test suite in an isolated subprocess and captures stdout, stderr, and exit code.

Parameters:

Name Type Description Default
state RuneState

Current agent state with generated code and test suite.

required

Returns:

Type Description
dict[str, Any]

State update dict with stdout, stderr, exit_code, and tests_passed keys.

Example

state = {"generated_code": "def fib(n): return n", "test_suite": ""} result = await execute_node(state) 'tests_passed' in result True

Source code in services/rune-agent/src/rune_agent/nodes.py
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
async def execute_node(state: RuneState) -> dict[str, Any]:
    """Execute the generated code in a sandboxed environment.

    Runs the generated code against the test suite in an isolated subprocess
    and captures stdout, stderr, and exit code.

    Args:
        state: Current agent state with generated code and test suite.

    Returns:
        State update dict with stdout, stderr, exit_code, and tests_passed keys.

    Example:
        >>> state = {"generated_code": "def fib(n): return n", "test_suite": ""}
        >>> result = await execute_node(state)
        >>> 'tests_passed' in result
        True
    """
    # Read env var inside function body so monkeypatch.setenv() works in tests
    timeout: int = int(os.environ.get("RUNE_EXEC_TIMEOUT", DEFAULT_TIMEOUT))

    script = state["generated_code"] + "\n\n" + state["test_suite"]

    # Auto-inject unittest.main() if TestCase classes exist but no runner
    if has_unittest_classes(script) and not re.search(r"unittest\.main\s*\(", script):
        script += (
            "\n\nimport unittest\nif __name__ == '__main__':\n    unittest.main()\n"
        )

    backend = get_sandbox_backend()
    result = backend.run(script, timeout=timeout)

    stdout = result.stdout
    stderr = result.stderr
    exit_code = result.exit_code

    # Determine test validation based on unittest output
    combined = state["generated_code"] + "\n" + state["test_suite"]
    has_tests = has_unittest_classes(combined)
    passed_count, total_count = count_test_results(stdout, stderr)
    tests_ran = total_count > 0

    if has_tests and tests_ran and exit_code == 0 and not result.is_timed_out:
        tests_passed = True
    elif has_tests and not tests_ran:
        tests_passed = False
    elif not has_tests:
        tests_passed = False
    else:
        tests_passed = False

    logger.info(
        "execute_node: exit_code=%d, tests_passed=%s, test_count=%d, tests_ran=%s",
        exit_code,
        tests_passed,
        total_count,
        tests_ran,
    )

    return {
        "stdout": stdout,
        "stderr": stderr,
        "exit_code": exit_code,
        "tests_passed": tests_passed,
        "test_count": total_count,
        "tests_ran": tests_ran,
    }
reflect_node async
reflect_node(state: RuneState) -> dict[str, Any]

Reflect on execution results and record the attempt in trajectory.

Increments the attempt counter and appends the current attempt's data to the trajectory list. Does not make any LLM calls.

Parameters:

Name Type Description Default
state RuneState

Current agent state with execution results.

required

Returns:

Type Description
dict[str, Any]

State update dict with incremented attempt_count and extended trajectory.

Example

state = {"attempt_count": 0, "generated_code": "def fib(n): pass", ... "exit_code": 1, "tests_passed": False, "trajectory": [], ... "stdout": "", "stderr": ""} result = await reflect_node(state) result['attempt_count'] 1

Source code in services/rune-agent/src/rune_agent/nodes.py
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
async def reflect_node(state: RuneState) -> dict[str, Any]:
    """Reflect on execution results and record the attempt in trajectory.

    Increments the attempt counter and appends the current attempt's data
    to the trajectory list. Does not make any LLM calls.

    Args:
        state: Current agent state with execution results.

    Returns:
        State update dict with incremented attempt_count and extended trajectory.

    Example:
        >>> state = {"attempt_count": 0, "generated_code": "def fib(n): pass",
        ...          "exit_code": 1, "tests_passed": False, "trajectory": [],
        ...          "stdout": "", "stderr": ""}
        >>> result = await reflect_node(state)
        >>> result['attempt_count']
        1
    """
    step: dict[str, Any] = {
        "generated_code": state["generated_code"],
        "stdout": state["stdout"],
        "stderr": state["stderr"],
        "exit_code": state["exit_code"],
        "tests_passed": state["tests_passed"],
    }

    # Use list concatenation (not .append()) — LangGraph requires immutable state
    new_trajectory: list[dict[str, Any]] = state["trajectory"] + [step]
    new_attempt_count = state["attempt_count"] + 1

    logger.info(
        "reflect_node: attempt=%d -> %d, trajectory_length=%d",
        state["attempt_count"],
        new_attempt_count,
        len(new_trajectory),
    )

    return {
        "attempt_count": new_attempt_count,
        "trajectory": new_trajectory,
    }
save_trajectory_node async
save_trajectory_node(state: RuneState) -> dict[str, Any]

Save the completed trajectory for parametric memory training.

Persists the trajectory to disk via record_trajectory() and determines the final outcome based on whether tests passed.

Parameters:

Name Type Description Default
state RuneState

Current agent state with complete trajectory and outcome.

required

Returns:

Type Description
dict[str, Any]

State update dict with outcome key ('success' or 'exhausted').

Example

state = {"tests_passed": True, "session_id": "abc", "trajectory": [], ... "task_description": "", "task_type": "", "adapter_ids": []} result = await save_trajectory_node(state) result['outcome'] 'success'

Source code in services/rune-agent/src/rune_agent/nodes.py
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
async def save_trajectory_node(state: RuneState) -> dict[str, Any]:
    """Save the completed trajectory for parametric memory training.

    Persists the trajectory to disk via record_trajectory() and determines
    the final outcome based on whether tests passed.

    Args:
        state: Current agent state with complete trajectory and outcome.

    Returns:
        State update dict with outcome key ('success' or 'exhausted').

    Example:
        >>> state = {"tests_passed": True, "session_id": "abc", "trajectory": [],
        ...          "task_description": "", "task_type": "", "adapter_ids": []}
        >>> result = await save_trajectory_node(state)
        >>> result['outcome']
        'success'
    """
    outcome = "success" if state["tests_passed"] else "exhausted"

    record_trajectory(
        session_id=state["session_id"],
        steps=state["trajectory"],
        outcome=outcome,
        task_description=state["task_description"],
        task_type=state["task_type"],
        adapter_ids=state["adapter_ids"],
    )

    logger.info(
        "save_trajectory_node: session_id=%s, outcome=%s",
        state["session_id"],
        outcome,
    )

    return {"outcome": outcome}

state

State definition for the Rune coding agent recursive loop.

Classes
RuneState

Bases: TypedDict

State for the Rune coding agent recursive loop.

Attributes:

Name Type Description
task_description str

Natural language description of the coding task.

task_type str

Category of task (e.g. 'function', 'class', 'refactor').

test_suite str

Test code that the generated solution must pass.

adapter_ids list[str]

LoRA adapter IDs to load for parametric memory.

session_id str

Unique identifier for trajectory persistence (UUID4).

attempt_count int

Current attempt number (0-indexed, incremented by reflect).

max_attempts int

Maximum number of generation attempts allowed.

generated_code str

Code produced by the generate node.

stdout str

Standard output from executing generated code.

stderr str

Standard error from executing generated code.

exit_code int

Process exit code from execution (0 = success).

tests_passed bool

Whether the test suite passed on this attempt.

test_count int

Number of unittest tests that ran.

tests_ran bool

Whether any unittest tests actually executed.

trajectory list[dict[str, Any]]

List of per-attempt records for parametric memory training.

outcome Optional[str]

Terminal result -- 'success', 'exhausted', or None if still running.