Acts of Emergence

008: Agent/Output

The _outputPath meta-property on a Call that specifies where to persist a tool's result. It's a string that tells the execution engine where to place the result, making it available for subsequent steps.

The agent system features two forms of output: the intermediate results of Tool Calls written to the context, and the Final Output of an entire iterative process.

Writing to Context with Output Path

While any Data Message can be a target, the most common use case is writing to a State Message to persist information across multiple steps of a process.

While any Data Message can serve as a scratchpad for a workflow, the Output Path meta-property on a Call is the primary mechanism for writing to it. When a Tool Call with this property is executed, the system appends its result to the context as a new message.

This new message is a standard Data Message, but it includes two additional pieces of metadata that are hidden from the LLM:

  • _call: The original Tool Call that generated this output.
  • _date: An ISO timestamp of when the output was written.
  • _outputMethod: The method specified in the original call, which defines how this data should be combined with other data.

This provides a complete, auditable trail of how the context was mutated, which is invaluable for debugging and tracing an agent's reasoning.

Defining the Output Path

The Output Path can be defined in two ways, allowing for a spectrum of control over a tool's behavior.

Dynamic Path (LLM-Decided)

In this mode, the LLM has the freedom to decide where to store the result at runtime, making the tool highly flexible.

// Tool schema allows any string for _outputPath
{
  "_outputPath": {
    "type": "string",
    "description": "Path to store the user summary.",
    "pattern": "^†"
  }
}

Prescribed Path (Hard-Coded)

This approach enforces a strict, predictable behavior, ensuring the tool always writes its output to a specific, hard-coded location.

// Tool schema locks _outputPath to a specific value
{
  "_outputPath": {
    "type": "string",
    "const": "†data.user.summary"
  }
}

Dynamic Variable Resolution

Crucially, these output messages are appended, not merged at write time. This design allows the final state of any variable to be constructed dynamically at read time, based on the _outputMethod stored in each message.

When a Variable Reference like †data.user.name needs to be resolved, the engine searches the context messages in reverse chronological order (newest to oldest).

  • If the resolver finds a message for the target path with an _outputMethod of set (or no method, as set is the default), it stops immediately. That message's value is the final value, and all older messages for that path are ignored. This is the "last-write-wins" behavior.
  • If the resolver finds messages with methods like merge, push, or concat, it continues searching backward, collecting all such messages until it either reaches a set message or the beginning of the context. It then reconstructs the final value by applying these collected operations in chronological order (oldest to newest).

This dynamic resolution ensures that the state is always consistent and accurately reflects the history of operations, enabling complex and reliable state manipulations.

Example: Appending and Resolving

1. Initial State

The context starts with an initial data message.

[
  {
    "type": "data",
    "data": { "user": { "name": "Alex", "status": "active" } }
  }
]

2. Tool Call Execution

A tool is called to update the user's status.

// Call being executed
{
  "_tool": "updateUserStatus",
  "newStatus": "inactive",
  "_outputPath": "†data.user.status"
}

3. Context After Execution

The engine appends a new message containing the output, which includes metadata about the call that produced it.

[
  // Original data message
  {
    "type": "data",
    "data": { "user": { "name": "Alex", "status": "active" } }
  },
  // Appended output message from the call
  {
    "type": "data",
    "data": { "user": { "status": "inactive" } },
    "_call": {
      "_tool": "updateUserStatus",
      "newStatus": "inactive",
      "_outputPath": "†data.user.status"
    },
    "_date": "2025-10-26T12:00:00Z"
  }
]

4. Variable Resolution

  • To resolve †data.user.status, the engine checks the last message first. It finds user.status and returns "inactive".
  • To resolve †data.user.name, the engine checks the last message, doesn't find user.name, then checks the previous message. It finds it there and returns "Alex"

The power of combining Variable References with Output Paths comes from their ability to define operations on data that is not yet available. For instance, a Tool Call can be defined to operate on a value from an Input message, even if that specific input has not been provided. This allows for the creation of reusable, parameterized workflows.

This concept extends to chaining Tool Calls together. A Tool Call can be created with a Variable Reference that points to the Output Path of a previous call in the same sequence. This creates a multi-step data flow where the output of one tool becomes the input for the next.

Calls Without an Output Path

Not every Tool Call needs to persist its result. The omission of the _outputPath property is a deliberate choice that signals different behaviors for latent and explicit calls.

Ephemeral Reasoning for Latent Calls

For a latent call, omitting the _outputPath allows it to function as an ephemeral reasoning step—a "thought" that informs subsequent actions within the same turn but is not saved to the persistent State. This is a technique for structuring an LLM's reasoning process.

For example, an agent can be designed to first use a latent think tool to analyze a problem and outline a strategy. This "thought" is not saved, but its generation immediately enriches the LLM's own internal context. In the very next step of the same solution, the LLM can then generate concrete, explicit Tool Calls that are more effective and better-aligned because of the preceding, un-persisted reasoning step.

Fire-and-Forget for Explicit Calls

For an explicit call to an Activity, omitting the _outputPath signals a "fire-and-forget" operation. The Execution Loop will invoke the Activity, but it will not wait for a result or store one in the context.

This is useful for side effects where a return value is not needed for the current workflow to proceed. Common examples include:

  • Logging an event to an external analytics service.
  • Sending a notification to a user or another system.
  • Triggering a long-running background process without needing to block the current plan.

Interactions with other systems

  • Data Message: The _outputPath is the primary mechanism for creating and updating Data Messages within a workflow. It transforms a stateless Tool Call into a stateful operation by persisting its result to the context, making it available for subsequent steps.

  • State Message: While any Data Message can be a target, the most common use case is writing to a State Message. This makes the State object the "scratchpad" for a workflow, allowing different tools to share information and build upon each other's results across multiple ticks of an Execution Loop.

  • Variable Reference: The Output Path is the direct counterpart to the Variable Reference. The Output Path writes data to the context, and the Variable Reference reads it. Together, they form a complete, declarative circuit for data flow, connecting the output of one tool to the input of another.

  • Expressions: Expressions introduce logic directly into the data flow wiring. By using || and && in an Output Path, a Tool Call can declare conditional outcomes or fan-out its result to multiple destinations. This moves the system away from rigid, hard-coded data pipelines toward a flexible structure that adapts to runtime conditions.

  • Plan: In the context of a Plan, Output Paths act as the "wires" that connect the different Tool Calls (the "nodes") into a complete data-flow graph. This allows an agent to define and execute an entire multi-step strategy as a single, declarative object.

  • Instancing: When a Tool Call includes an _instance property, any _outputPath it specifies is automatically scoped to that specific instance's data context. This ensures data isolation in parallel processing, guaranteeing that the output of a tool operating on one instance will not interfere with the state of another.

From Ephemeral Outputs to Persistent State

The Output Path mechanism provides a way to manage the flow of data between individual Tool Calls, and it can target any Data Message. However, to build complex, multi-step agents that can reason and adapt over time, a specialized form of memory is required—a message type specifically designed to persist information across multiple, independent requests.

009: Agent/State describes the protocol for this persistent memory.