# ToolRuntime in @maka/runtime: Core Responsibilities and Architecture Explained

> Explore the core responsibilities and architecture of ToolRuntime in @maka/runtime. Understand how it orchestrates tool lifecycles, enforces safety, and ensures reliable execution.

- Repository: [The Apache Software Foundation/maka](https://github.com/apache/maka)
- Tags: architecture
- Published: 2026-08-26

---

**The `ToolRuntime` in `@maka/runtime` serves as the central orchestrator that manages tool lifecycle, enforces safety boundaries, coordinates sandbox requests, limits concurrency, and ensures durable, observable execution of all tool calls within a Maka session.**

The `ToolRuntime` class provides the Apache Maka platform with a deterministic, sandboxed environment for executing tools such as web-search, apply-patch, sub-agents, and user-question prompts. Defined in [`packages/runtime/src/tool-runtime.ts`](https://github.com/apache/maka/blob/main/packages/runtime/src/tool-runtime.ts), this runtime acts as a single source of truth for tool semantics, handling everything from admission control to durable commit boundaries. Its architecture ensures that every tool invocation is authorized, recorded, and resource-bounded.

## Turn and Lifecycle Management

The runtime creates a fresh `ToolRuntime` instance for each turn, encapsulating all state for that specific execution context. When a turn ends, the `endTurn` method (lines 669‑730) orchestrates a graceful shutdown sequence: it settles or rejects every outstanding sandbox-boundary request and user-question promise, awaits all in-flight tool settlements, and then invokes `resetTurnState` (lines 809‑833) to clear internal buffers before the next turn begins.

This lifecycle management prevents state leakage between turns and ensures that dangling asynchronous operations are properly cleaned up. The runtime maintains registries for pending operations, allowing it to force settlement when thresholds for denials or unresolved rounds are reached.

## Tool Invocation Coordination

At the heart of the runtime lies the tool-settlement pipeline. The `settleToolCall` and `settleToolCallRaw` methods (starting at line 733) transform a concrete `MakaTool` implementation into a durable `ToolResult`. This process flows through `performToolSettlement` (lines 778‑795), which handles the actual execution and result formatting.

When tools fail, the runtime writes synthetic "tool_result" events via `writeSyntheticToolResult` (lines 916‑981) to ensure the session ledger remains complete. For successful executions, it emits standard events and records telemetry through `recordToolInvocation`. The runtime also enforces result-size limits by calling `truncateToolOutput` (around line 773) to prevent oversized payloads from reaching the model.

## Admission Control and Gating

Before any tool executes, `admitToolForStep` (called around line 998) validates the request against admission policies. The runtime enforces **exclusive-step** and **direct-only** rules to control which tools may run in specific contexts. A **loop-gate** mechanism tracks identical failures via `recordLoopGateOutcome` (lines 884‑914) and blocks tools after a configurable threshold, preventing infinite retry loops.

Per-step `ToolGating` hides tools that are not searchable in the current step, allowing the runtime to dynamically restrict the tool palette based on execution context. This gating ensures that agents only access appropriate capabilities for their current task phase.

## Sandbox Boundaries and User Questions

The runtime manages two distinct `AwaitRegistry` instances: one for sandbox-boundary requests and one for user-question answers. These registries track pending promises that require external resolution. The methods `respondToSandboxBoundaryResponse` (lines 639‑676) and `respondToUserQuestion` (lines 391‑408) provide the entry points for settling these requests when external systems or users provide responses.

This architecture allows the runtime to pause tool execution safely while awaiting human input or external API callbacks, maintaining clear separation between the tool execution context and the host environment.

## Sub-Agent and Concurrency Limits

Resource exhaustion is prevented through hard concurrency caps. The runtime defines `MAX_ACTIVE_SUBAGENT_TOOLS_PER_TURN` and `MAX_ACTIVE_CHILD_AGENT_RUNS_PER_TURN` (lines 219‑221) to limit simultaneous executions. An `AdmissionLimiter` instance, initialized as `childAgentRunLimiter` (line 220), tracks active child-agent runs and rejects new requests when capacity is reached.

These limits ensure that a single session cannot spawn unbounded subprocesses, protecting both the host system and the model's context window from being overwhelmed by concurrent tool outputs.

## Durable Tool Attempts and Commit Boundaries

When a durable commit sink is available, the runtime creates a `DurableToolAttempt` (lines 746‑754) to track the tool's outcome across T1 and T2 commit phases. This pattern assigns a durable operation ID to each tool call, allowing the system to persist results and recover from failures without re-executing expensive operations.

The runtime handles `RuntimeCommitBoundaryError` exceptions during this process, ensuring that commit failures are recorded in the session ledger and do not leave the execution state inconsistent.

## Context Provision and Error Classification

Every running tool receives a rich execution context via the `MakaToolContext` interface (lines 191‑267). This context includes session IDs, abort signals, current working directory, permission mode, output emitters, child-agent spawners, and sandbox-boundary request helpers. The runtime constructs this context inside `executeTool` (around line 975), injecting all necessary dependencies while maintaining sandbox isolation.

Error handling leverages `classifyError` to categorize provider-side failures and `truncateToolOutput` to trim oversized results. The runtime formats standardized error messages, ensuring consistent behavior across different tool implementations and failure modes.

## Implementation Examples

The following patterns demonstrate typical usage of the `ToolRuntime` class.

```typescript
import { ToolRuntime, type ToolRuntimeInput } from '@maka/runtime/tool-runtime';

// Build a minimal ToolRuntimeInput (normally supplied by the backend)
const input: ToolRuntimeInput = {
  sessionId: 'sess-123',
  header: { /* … */ },
  connection: { providerType: 'openai', /* … */ },
  modelId: 'gpt-4o',
  appendMessage: async (msg) => {/* forward to LLM */},
  readExecutionBoundary: async () => ({ /* boundary data */}),
  newId: () => crypto.randomUUID(),
  now: () => Date.now(),
  getPermissionPauseTarget: () => null,
  turnId: 'turn-1',
};

// Create a runtime for the current turn
const runtime = new ToolRuntime(input);

// Define a simple tool
const echoTool: MakaTool = {
  name: 'echo',
  description: 'Returns the same string it receives.',
  parameters: { type: 'object', properties: { text: { type: 'string' } }, required: ['text'] },
  impl: ({ text }: { text: string }, ctx) => text,
};

// Execute the tool inside the turn
const call: ResolvedMakaToolCall = {
  tool: echoTool,
  turnId: input.turnId,
  toolCallId: 'call-001',
  input: { text: 'Hello, Maka!' },
  abortSignal: new AbortController().signal,
  eventSink: { push: () => {}, pushAndWaitUntilConsumed: async () => {} },
};

runtime.settleToolCall(call).then(({ result, modelOutput }) => {
  console.log('Result:', result);               // → Hello, Maka!
  console.log('Model output:', modelOutput);    // → { type: 'json', value: { text: 'Hello, Maka!' } }
});

```

Handling user questions requires tracking the pending request in the runtime's registry:

```typescript
// Emit a user question via the runtime's message channel
runtime.input.appendMessage({
  type: 'tool_call',
  id: 'q-001',
  turnId: input.turnId,
  toolName: 'ask_user_question',
  args: { question: 'What is your name?' },
});

// Later, resolve the pending promise
runtime.respondToUserQuestion({
  requestId: 'q-001',
  answers: ['Alice'],
});

```

Finalizing a turn ensures all pending operations settle:

```typescript
// Settles any pending sandbox-boundary requests and awaits in-flight tools
await runtime.endTurn('completed');

```

## Summary

The `ToolRuntime` in `@maka/runtime` centralizes tool execution management through eight core responsibility areas:

- **Turn Lifecycle**: Creates per-turn instances and ensures clean state resets via `endTurn` and `resetTurnState`.
- **Invocation Coordination**: Manages tool settlement pipelines, synthetic event writing, and telemetry recording.
- **Admission Control**: Enforces exclusive-step rules and loop-gate thresholds to prevent inappropriate or repetitive tool use.
- **Sandbox Management**: Maintains `AwaitRegistry` instances for boundary requests and user questions with dedicated response handlers.
- **Concurrency Limits**: Applies `MAX_ACTIVE_SUBAGENT_TOOLS_PER_TURN` and `MAX_ACTIVE_CHILD_AGENT_RUNS_PER_TURN` caps through `AdmissionLimiter`.
- **Durability**: Creates `DurableToolAttempt` records for T1/T2 commit phases when persistent sinks are available.
- **Context Injection**: Builds comprehensive `MakaToolContext` objects containing session metadata, abort signals, and permission states.
- **Safety Boundaries**: Classifies errors, truncates oversized outputs, and standardizes failure event formats.

Collectively, these mechanisms ensure every tool call is **authorized**, **durably recorded**, **observable**, and **resource-bounded**.

## Frequently Asked Questions

### How does ToolRuntime handle tool failures?

When a tool fails, the runtime captures the error through `performToolSettlement` and invokes `writeSyntheticToolResult` (lines 916‑981) to emit a structured "tool_result" event containing the failure reason. The runtime uses `classifyError` to categorize the failure type and applies `truncateToolOutput` to ensure error messages remain within size limits. This synthetic event writing guarantees that the session ledger remains complete even when tools throw exceptions or return invalid results.

### What is the purpose of the loop-gate mechanism?

The loop-gate prevents infinite retry cycles by tracking identical failures via `recordLoopGateOutcome` (lines 884‑914). When a tool fails repeatedly with the same error pattern, the runtime blocks further admissions after a configurable threshold, forcing the agent to select alternative strategies. This protection is essential for autonomous systems that might otherwise spin on broken dependencies or invalid parameters.

### How does ToolRuntime manage concurrent sub-agent executions?

The runtime enforces hard limits through constants defined at lines 219‑221: `MAX_ACTIVE_SUBAGENT_TOOLS_PER_TURN` and `MAX_ACTIVE_CHILD_AGENT_RUNS_PER_TURN`. An `AdmissionLimiter` instance named `childAgentRunLimiter` (line 220) tracks active child processes. When a new sub-agent request exceeds these limits, the runtime rejects the admission before spawning the process, preventing resource exhaustion and context window overflow.

### What is the difference between sandbox-boundary requests and user questions?

**Sandbox-boundary requests** are asynchronous operations requiring external system interaction (such as file system access or network calls) managed through `respondToSandboxBoundaryResponse` (lines 639‑676). **User questions** are explicit prompts for human input handled via `respondToUserQuestion` (lines 391‑408). While both use `AwaitRegistry` for promise management, sandbox boundaries enforce permission checks and denial counting, whereas user questions typically block execution until the user provides an answer through the UI.