> ## Documentation Index
> Fetch the complete documentation index at: https://helpcenter.magure.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Memory and variable store

> Persist and access data within and across workflow executions using MagOneAI's variable store

## Variable Store system

The **Variable Store** is the mechanism for passing data between activities within a workflow and persisting state. Think of it as a key-value store scoped to each workflow execution, where every activity can read from and write to shared data.

Understanding the variable store is crucial for building effective workflows. It's how context flows through your workflow, how agents share information, and how decisions are made based on accumulated data.

## How the variable store works

The variable store provides a shared data layer for workflow execution.

<Steps>
  <Step title="Workflow starts">
    A new variable store is created for the workflow execution. It starts empty except for the trigger input data.
  </Step>

  <Step title="Input and context stored">
    The execution's input is stored in the `input` scope and its runtime context in the `system` scope:

    ```json theme={null}
    {
      "input": { "customer_id": "CUST-123", "...": "..." },
      "system": { "user_id": "...", "execution_id": "..." }
    }
    ```
  </Step>

  <Step title="Activities execute and write">
    As each activity completes, its output fields are stored in the variable store under the activity's id, flat (not wrapped in an `output` key):

    ```json theme={null}
    {
      "input": {...},
      "document_agent": {
        "extracted_text": "...",
        "confidence": 0.95
      }
    }
    ```

    The most recent activity's output is also mirrored to the reserved `_prev` scope, which powers implicit chaining into the next activity.
  </Step>

  <Step title="Subsequent activities read">
    Later activities read from the variable store using variable references:

    ```javascript theme={null}
    {{document_agent.extracted_text}}
    ```
  </Step>

  <Step title="Data accumulates">
    As the workflow progresses, more data accumulates in the variable store, creating rich context for later activities.
  </Step>

  <Step title="Workflow completes">
    When the workflow finishes, the final variable store state is preserved in execution history. You can inspect it for debugging and auditing.
  </Step>
</Steps>

<Info>
  Each workflow execution has its own isolated variable store. Multiple concurrent executions of the same workflow don't share data — each has its own independent context.
</Info>

## Setting variables

Variables are written to the store automatically by activity outputs, but you can also set them explicitly.

### Automatic activity outputs

By default, each activity's output fields are stored under the activity's id:

**Activity id:** `document_extractor`

**Activity output:**

```json theme={null}
{
  "extracted_text": "...",
  "metadata": {
    "pages": 12,
    "language": "en"
  },
  "confidence": 0.95
}
```

**Variable store:**

```json theme={null}
{
  "document_extractor": {
    "extracted_text": "...",
    "metadata": {...},
    "confidence": 0.95
  }
}
```

Reference these fields as `{{document_extractor.extracted_text}}` and `{{document_extractor.metadata.pages}}`.

### Custom variable names via output mapping

Customize how activity outputs are stored:

**Output mapping:**

```json theme={null}
{
  "extracted_text": "{{agent.text}}",
  "document_language": "{{agent.metadata.language}}",
  "extraction_confidence": "{{agent.confidence}}"
}
```

**Variable store:**

```json theme={null}
{
  "extracted_text": "...",
  "document_language": "en",
  "extraction_confidence": 0.95
}
```

This creates cleaner, more accessible variable names for downstream activities.

### Manual variable setting within prompts

Agent prompts can explicitly set variables:

**Agent instruction:**

```
Analyze the document and set the following variables:
- document_type: The type of document (invoice, contract, etc.)
- risk_level: low, medium, or high
- requires_review: true if human review is needed
```

The agent's structured output sets these variables directly in the variable store.

## Getting variables

Access data from the variable store using the `{{variable_path}}` syntax.

### Basic variable references

**Syntax:** `{{key.nested.field}}`

**Examples:**

```javascript theme={null}
// Execution input (input scope) and runtime context (system scope)
{{input.customer_id}}
{{input.document_url}}
{{system.user_id}}

// Activity output (flat under the activity id, no .output wrapper)
{{agent_name.field_name}}
{{tool_name.result}}

// Nested fields
{{compliance_agent.analysis.risk_score}}
{{document_agent.metadata.pages}}
```

### Accessing arrays

**Array element by numeric dot segment:**

```javascript theme={null}
{{agent.findings.0}}
{{agent.findings.1.severity}}
```

List elements are addressed by a numeric path segment (`findings.0`), not bracket notation. An index that is out of range or non-numeric resolves to null.

### Accessing objects

**Object field:**

```javascript theme={null}
{{agent.customer.name}}
{{agent.customer.contact.email}}
```

**All object properties:**

```javascript theme={null}
{{agent.customer}}
// Returns the entire customer object
```

### Fallback paths

Use `||` to fall back to another **path** when the first one resolves to null. The first non-null value wins:

```javascript theme={null}
{{agent.score || defaults.score}}
{{primary.email || secondary.email}}
```

Both sides are paths, not literal defaults. A reference that doesn't resolve returns null.

### Missing values

To branch on whether a value is present, use a [Condition node](/workflows/condition-node) with the `exists` / `not_exists` operator, or the `||` fallback above to substitute another path.

## Variable scope

Variables exist at different scopes within a workflow.

### Workflow-level scope

Available throughout the entire workflow execution.

**Variables at workflow scope:**

* Execution input: `{{input.*}}`
* Runtime context: `{{system.*}}`
* All activity outputs: `{{activity_id.*}}`
* The previous activity's output: `{{_prev.*}}`
* Custom variables set via output mapping

<Note>
  `input`, `system`, and `_prev` are reserved scopes; an activity cannot use those ids. Everything else is keyed by activity id.
</Note>

**Example:**

```
Activity 1: Extract document
  → Sets: {{extracted_text}}

Activity 2: Analyze compliance (10 steps later)
  → Reads: {{extracted_text}}
  ✓ Available across entire workflow
```

### Branch scope (Condition and Parallel nodes)

Variables within branches have special considerations.

**Condition branches:**

Each activity inside a branch writes to the variable store under its own id, and those outputs stay available downstream after the condition rejoins:

```
Condition (variable): risk.score greater_than 0.8
  ├─ true  → Agent "detailed_analysis"  → {{detailed_analysis.summary}}
  └─ false → Agent "standard_analysis"  → {{standard_analysis.summary}}

Next activity after the branches rejoin:
  → Reads whichever branch ran, e.g. {{detailed_analysis.summary}}
```

Only the branch that executed writes its outputs, so guard downstream reads with a [Condition node](/workflows/condition-node) `exists` check when a value may be absent.

**Parallel branches:**

Each parallel branch is itself an activity that writes its output under its own id. The [Parallel node](/workflows/parallel-node) also merges the branch results into a single variable named by its `output_variable` config (default `parallel_results`):

```
Parallel node with branches: technical_review, financial_review, legal_review

Next activity reads either the individual branch outputs:
  → {{technical_review.score}}
  → {{financial_review.score}}
or the merged result:
  → {{parallel_results}}
```

### Loop scope (ForEach nodes)

A [ForEach node](/workflows/foreach-node) runs its body once per item in a collection. The current item is exposed under the variable name you set in the node's `item_variable` config, and per-item inputs are wired through the node's `input_mapping`. See the ForEach node page for the exact per-item references.

```
ForEach with item_variable: "document"

Loop body reads the current item as:
  → {{document}}
```

### Sub Use Case scope

Child workflows have their own isolated variable stores.

**Parent workflow variable store:**

```json theme={null}
{
  "input": {...},
  "parent_agent": {...}
}
```

**Child workflow variable store (independent):**

```json theme={null}
{
  "input": {...},  // Input passed from the parent
  "child_agent": {...}
}
```

Parent and child don't share variable stores. Data flows explicitly through input/output mapping.

## Variable store structure

Understanding the structure helps you access data efficiently.

### Complete variable store example

```json theme={null}
{
  "input": {
    "customer_id": "CUST-12345",
    "document_url": "https://bucket.s3.com/doc.pdf",
    "urgency": "high"
  },

  "system": {
    "user_id": "...",
    "execution_id": "..."
  },

  "document_extractor": {
    "extracted_text": "...",
    "document_type": "invoice",
    "metadata": {
      "pages": 3,
      "language": "en",
      "confidence": 0.95
    }
  },

  "compliance_check": {
    "is_compliant": true,
    "risk_score": 0.3,
    "findings": [
      "All required fields present",
      "No anomalies detected"
    ]
  },

  "financial_analysis": {
    "score": 90,
    "amount": 50000,
    "currency": "AED"
  },

  "final_decision": {
    "approved": true,
    "confidence": 0.92,
    "next_steps": [...]
  }
}
```

### Accessing nested data

```javascript theme={null}
// Execution input
{{input.customer_id}}  // "CUST-12345"

// Nested object
{{document_extractor.metadata.pages}}  // 3

// Array element by numeric segment
{{compliance_check.findings.0}}  // "All required fields present"

// Another activity's output field
{{financial_analysis.amount}}  // 50000

// Deep nesting
{{final_decision.next_steps.0.action}}
```

## Use cases

Understanding when and how to use the variable store effectively.

### Passing context between distant nodes

**Scenario:** A node late in the workflow needs data from an early node.

```
Node 1: Extract customer data
  → Output: {{customer_data}}

Node 2-10: Various processing steps
  (Don't use customer_data)

Node 11: Send personalized email
  → Input: {{customer_data.email}}
  ✓ Data preserved across 10 intermediate steps
```

The variable store maintains all data throughout execution, so later nodes can access early outputs.

### Accumulating results from parallel branches

**Scenario:** Multiple agents analyze the same document; combine their insights.

```
Parallel node: Document analysis
  ├─ Branch 1: Technical analysis → {{technical_score}}
  ├─ Branch 2: Financial analysis → {{financial_score}}
  ├─ Branch 3: Legal analysis → {{legal_score}}
  └─ Branch 4: Compliance analysis → {{compliance_score}}

Synthesis agent:
  Input: {
    technical: "{{technical_analysis.score}}",
    financial: "{{financial_analysis.score}}",
    legal: "{{legal_analysis.score}}",
    compliance: "{{compliance_analysis.score}}"
  }
```

The variable store collects outputs from all branches for easy synthesis.

### Building up a final report

**Scenario:** Accumulate findings throughout the workflow for a final report.

```
Workflow: Due diligence analysis

Step 1: Company research
  → Output: {{company_profile}}

Step 2: Financial analysis
  → Output: {{financial_assessment}}

Step 3: Legal review
  → Output: {{legal_findings}}

Step 4: Market analysis
  → Output: {{market_position}}

Step 5: Report generation agent
  Input: {
    company_profile: "{{company_profile}}",
    financial_assessment: "{{financial_assessment}}",
    legal_findings: "{{legal_findings}}",
    market_position: "{{market_position}}"
  }
  Output: Comprehensive due diligence report
```

Each step contributes data to the variable store; the final report agent synthesizes everything.

### Conditional routing based on accumulated data

**Scenario:** Route based on multiple factors from different activities.

```
Step 1: Extract document data
  → Output: {{amount}}, {{document_type}}

Step 2: Risk assessment
  → Output: {{risk_score}}

Step 3: Compliance check
  → Output: {{is_compliant}}

Step 4: Condition node
  Condition:
    {{amount}} > 50000 AND
    {{risk_score}} > 0.7 AND
    {{is_compliant}} == true

  ├─ True: Escalate to manager
  └─ False: Auto-approve
```

The condition evaluates data from multiple previous activities stored in the variable store.

## Cross-execution persistence

The variable store is normally scoped to a single workflow execution, but you can persist data across multiple runs.

### Workflow-level state

For state that needs to persist across workflow runs, use external storage:

**Pattern:**

```
Workflow execution 1:
  → Process data
  → Tool: "Store result in database"
    Key: "workflow_state"
    Value: {{accumulated_data}}

Workflow execution 2:
  → Tool: "Retrieve state from database"
    Key: "workflow_state"
  → Continue processing with previous state
```

### Use cases for persistent state

<CardGroup cols={1}>
  <Card title="Running totals" icon="calculator">
    Accumulate totals across multiple workflow runs.

    **Example:** Track total processed invoices, cumulative amounts
  </Card>

  <Card title="State machines" icon="diagram-project">
    Maintain state across workflow executions.

    **Example:** Customer onboarding progress (stage 1 → stage 2 → stage 3)
  </Card>

  <Card title="Historical context" icon="clock-rotate-left">
    Access results from previous executions.

    **Example:** Compare current analysis to previous runs for trend detection
  </Card>

  <Card title="Caching" icon="database">
    Store computed results for reuse in future executions.

    **Example:** Cache customer research that doesn't change frequently
  </Card>
</CardGroup>

### Implementation with HashiCorp Vault

MagOneAI integrates with HashiCorp Vault for secure persistent storage:

**Store data:**

```
Tool: "Vault Write"
Input: {
  path: "workflow_state/customer_onboarding/{{customer_id}}",
  data: {
    stage: "document_verification",
    completed_steps: ["registration", "email_verification"],
    pending_documents: ["passport", "proof_of_address"]
  }
}
```

**Retrieve data:**

```
Tool: "Vault Read"
Input: {
  path: "workflow_state/customer_onboarding/{{customer_id}}"
}
Output: {{vault_data.data}}
```

**Use in workflow:**

```
Condition: {{vault_data.data.stage}} == "document_verification"
  ├─ True: Continue from where we left off
  └─ False: Start from beginning
```

## Best practices

<AccordionGroup>
  <Accordion title="Use descriptive variable names">
    Choose variable names that indicate source and content.

    **Good:**

    * `compliance_agent.risk_assessment`
    * `document_extractor.extracted_text`
    * `financial_analysis.total_amount`

    **Poor:**

    * `result1`
    * `output`
    * `data`
  </Accordion>

  <Accordion title="Structure activity outputs consistently">
    Use consistent output structures across similar activities. This makes workflows easier to understand and maintain.

    **Standard structure:**

    ```json theme={null}
    {
      "success": true,
      "data": {...},
      "metadata": {
        "confidence": 0.95,
        "processing_time_ms": 1500
      }
    }
    ```
  </Accordion>

  <Accordion title="Check variable existence before use">
    A reference that doesn't resolve returns null. When a value may be absent, gate on it with a [Condition node](/workflows/condition-node) `exists` operator before the activity that needs it, rather than assuming it is set.
  </Accordion>

  <Accordion title="Use output mapping for cleaner variable names">
    Instead of deeply nested paths, map to cleaner top-level variables:

    ```json theme={null}
    {
      "customer_email": "{{agent.customer.contact.primary_email}}",
      "risk_score": "{{agent.analysis.risk_assessment.final_score}}"
    }
    ```

    Then use: `{{customer_email}}` instead of `{{agent.customer.contact.primary_email}}`
  </Accordion>

  <Accordion title="Document variable contracts">
    For reusable workflows (Sub Use Cases), document the expected input variables and guaranteed output variables. This is the workflow's "contract."

    **Example:**

    ```yaml theme={null}
    Inputs:
      - document_url: string (required)
      - document_type: string (optional)

    Outputs:
      - verified: boolean
      - confidence: number (0-1)
      - extracted_data: object
    ```
  </Accordion>

  <Accordion title="Minimize data stored in variables">
    Don't store large documents or images directly in variables. Store URLs or references instead.

    **Good:** `{{document_url}}`
    **Poor:** `{{base64_encoded_document}}` (can be megabytes)
  </Accordion>

  <Accordion title="Use semantic keys for parallel branches">
    Name parallel branches semantically so their outputs are self-documenting:

    ```
    Parallel node: "document_analysis"
      ├─ Branch: "technical_review"
      ├─ Branch: "financial_review"
      └─ Branch: "legal_review"

    Access: {{technical_review.score}}
    ```
  </Accordion>
</AccordionGroup>

<Tip>
  View the complete variable store for any workflow execution in MagOneAI Studio's execution history. This is invaluable for debugging — you can see exactly what data was available at each step.
</Tip>

## Debugging with the variable store

The variable store is your primary debugging tool for workflows.

### Viewing variable store in execution history

For any completed or running workflow execution:

1. Open execution details in MagOneAI Studio
2. Navigate to "Variable Store" tab
3. See the complete variable store state at each activity

**What you can see:**

* Initial state (trigger input)
* State after each activity
* Final state
* Variables that were read vs written at each step

### Common debugging patterns

**Problem: Activity not receiving expected input**

→ Check variable store before the activity. Does the variable exist? Is the path correct?

**Problem: Condition routing incorrectly**

→ Check variable store at the Condition node. What values is it comparing?

**Problem: Missing data in final output**

→ Trace backward through the variable store. Which activity should have set this variable? Did it run? Did it produce output?

**Problem: Parallel branches not working as expected**

→ Check variable store after parallel completion. Did all branches complete? Are outputs structured correctly?

## Blackboard store

Alongside the variable store, an execution has a **Blackboard**: a run-scoped store of the intermediate results an agent produces as it works, so those results survive the run instead of living only in the in-memory tool-loop. Where the variable store holds each activity's declared output keyed by activity id, the Blackboard captures the fuller detail behind an agent's steps, its tool results and sub-agent outputs, and makes them searchable within that one execution.

### What it holds

As an agent runs, its intermediate results are written to the Blackboard as **artifacts** (one per tool result or captured agent output). Each artifact's body is stored durably and indexed for retrieval, scoped to the current execution. Artifacts accumulate across the run, including across loop re-entries, and are retained for a limited window before cleanup.

### Querying with `__query_blackboard`

When enabled, an agent gets a `__query_blackboard` recall tool. It lets the agent retrieve the **full result of something it already did earlier this run**, a prior search, page fetch, or query, instead of repeating the tool call:

* The agent supplies a `query` (what to recall) and an optional `top_k` (1 to 10, default 5).
* The search is restricted to the **current execution's** artifacts. The execution id is taken from the run context, never from a model-supplied argument, so an agent cannot reach another execution's or another tenant's data.
* Recall is best-effort: it returns the most relevant earlier results, and the agent decides when to use it.

<Note>
  The Blackboard recall tool is exposed per agent through the agent's **query blackboard** capability (off by default), and depends on the deployment having harness retrieval enabled. When either is off, the tool simply isn't offered and the run is unaffected.
</Note>

### Blackboard vs the variable store

<Info>
  Use the **variable store** for deterministic hand-offs between nodes, an activity references another activity's output by an exact path (`{{activity_id.field}}`). Use the **Blackboard** when an agent needs to search back over the detail of its own earlier work in the same run without re-running a tool. Both are scoped to a single execution and cleared between runs; neither persists across executions (that is [conversational memory](#conversational-memory)).
</Info>

## Conversational memory

In addition to the per-execution variable store, MagOneAI supports **conversational memory**. This enables agents to remember facts, preferences, and context across multiple workflow executions.

Conversational memory is **off by default** — a platform admin enables the capability for the deployment, and you then turn it on per agent with the agent's **memory** capability toggle.

### How conversational memory works

When memory is enabled for an agent:

1. **Memory retrieval** — Before the agent executes, relevant memories for the current user are retrieved and selected by similarity to the current request
2. **Context injection** — Retrieved memories are added to the agent's prompt, giving it awareness of past interactions
3. **Memory extraction** — After the agent completes, new facts and preferences are extracted from the conversation and stored

### Memory scope

Retrieved memory is scoped **per user**: the facts and preferences an agent recalls belong to the user who triggered the workflow, and they carry across that user's conversations.

<Warning>
  Retrieval is keyed to the **user**, not to a single project or agent. A user's remembered facts can surface to any memory-enabled agent that user interacts with, across projects. Keep this in mind for sensitive context — don't rely on memory to isolate information between projects. (Stored memories do record their originating organization, project, and agent, which the memory-management views can filter by, but those filters are not applied when memory is injected at run time.)
</Warning>

### When to use conversational memory

* **Customer support agents** — Remember customer preferences and past issues
* **Personal assistants** — Retain user preferences across sessions
* **Onboarding flows** — Remember progress and context from previous interactions

<Info>
  Conversational memory is different from the variable store. The variable store is scoped to a single execution and holds workflow data. Conversational memory persists across executions and holds user-level facts and preferences.
</Info>

## Next steps

<CardGroup cols={2}>
  <Card title="Agent node" icon="robot" href="/workflows/agent-node">
    Learn how agents read from and write to the variable store
  </Card>

  <Card title="Condition node" icon="code-branch" href="/workflows/condition-node">
    Use variable store data in conditional routing
  </Card>

  <Card title="Parallel node" icon="code-branch" href="/workflows/parallel-node">
    Understand how parallel outputs merge into the variable store
  </Card>

  <Card title="ForEach node" icon="repeat" href="/workflows/foreach-node">
    Access special loop variables in the variable store
  </Card>
</CardGroup>
