# DataRef — Attaching Live Data to AI Messages

DataRef lets you attach structured data from the database directly into an AI message. The backend fetches it, renders it as clean XML, and injects it into the model's context automatically — you never deal with the XML yourself.

---

## Quick Start

```typescript
import { DataRef, buildDataInputBlock } from "@/utils/data_ref_utils";

// Build a content block with one or more data references
const dataBlock = buildDataInputBlock([
  DataRef.record("projects", projectId),
  DataRef.query("tasks", {
    filter: { project_id: projectId, status: "incomplete" },
    fields: ["id", "title", "priority", "due_date"],
    sort: { field: "due_date", direction: "asc" },
    limit: 20,
    label: "Open Tasks",
  }),
]);

// Include it in the message content array alongside the user's text
const content = [
  { type: "input_text", text: userMessage },
  dataBlock,
];
```

---

## The Three Ref Types

### `DataRef.record` — One full record

Attach a single row from any allowed table.

```typescript
DataRef.record(table, id, opts?)
```

```typescript
// Full record, all allowed fields
DataRef.record("projects", "4e34e88a-...")

// Specific fields only
DataRef.record("projects", "4e34e88a-...", {
  fields: ["name", "description", "status"],
  label: "Current Project",
})
```

**Model sees:**
```xml
<record table="projects" label="Current Project" id="4e34e88a-...">
  <name>AI Voice Assistant</name>
  <description>Build a Siri-like agent...</description>
  <status>active</status>
</record>
```

---

### `DataRef.query` — Multiple rows with filters

Fetch a filtered, sorted list of rows from any allowed table.

```typescript
DataRef.query(table, opts?)
```

```typescript
// All incomplete tasks in a project, sorted by due date
DataRef.query("tasks", {
  filter: { project_id: "4e34e88a-...", status: "incomplete" },
  fields: ["id", "title", "priority", "due_date"],
  sort: { field: "due_date", direction: "asc" },
  limit: 20,
  label: "Open Tasks",
})

// All public notes (no filter = all rows up to limit)
DataRef.query("notes", {
  filter: { is_public: true },
  limit: 10,
})
```

**Model sees:**
```xml
<records table="tasks" label="Open Tasks" count="3">
  <row id="..."><title>Design API</title><priority>high</priority><due_date>2026-04-01</due_date></row>
  <row id="..."><title>Write tests</title><priority>medium</priority><due_date>2026-04-05</due_date></row>
  <row id="..."><title>Deploy</title><priority>low</priority><due_date>2026-04-10</due_date></row>
</records>
```

---

### `DataRef.field` — One field value

Attach a single value when you don't need the entire record.

```typescript
DataRef.field(table, id, field_name, opts?)
```

```typescript
// Just the project description
DataRef.field("projects", "4e34e88a-...", "description", {
  label: "Project Goal",
})
```

**Model sees:**
```xml
<field table="projects" record_id="4e34e88a-..." name="description" label="Project Goal">
  Build a Siri-like agent for Matrx with voice-to-action capabilities.
</field>
```

---

## Multiple Refs in One Block

You can mix ref types freely. Everything resolves concurrently and is wrapped in a single `<data_context>` block.

```typescript
buildDataInputBlock([
  DataRef.record("projects", projectId, { label: "Project" }),
  DataRef.query("tasks", {
    filter: { project_id: projectId, status: "incomplete" },
    label: "Open Tasks",
    limit: 10,
  }),
  DataRef.field("workspaces", workspaceId, "name", { label: "Workspace" }),
])
```

**Model sees:**
```xml
<data_context>

<record table="projects" label="Project" id="...">
  <name>AI Voice Assistant</name>
  <status>active</status>
</record>

<records table="tasks" label="Open Tasks" count="2">
  <row id="..."><title>Design API</title><priority>high</priority></row>
  <row id="..."><title>Write tests</title><priority>medium</priority></row>
</records>

<field table="workspaces" record_id="..." name="name" label="Workspace">
  Product Team
</field>

</data_context>
```

---

## Block Options

```typescript
buildDataInputBlock(refs, {
  // If true, a fetch failure drops the whole block silently instead of erroring.
  optional_context: true,

  // If true, backend re-fetches on every turn (for live data like task status).
  keep_fresh: true,
})
```

Individual refs also support `optional_context` for per-ref resilience:

```typescript
DataRef.record("contacts", contactId, { optional_context: true })
```

---

## Allowed Tables

| Table | Available Fields |
|-------|----------------|
| `notes` | `id`, `label`, `content`, `folder_name`, `tags`, `is_public`, `created_at`, `updated_at` |
| `tasks` | `id`, `title`, `description`, `status`, `priority`, `due_date`, `project_id`, `parent_task_id`, `assignee_id`, `is_public`, `created_at`, `updated_at` |
| `projects` | `id`, `name`, `description`, `status`, `workspace_id`, `organization_id`, `created_at`, `updated_at` |
| `workspaces` | `id`, `name`, `description`, `organization_id`, `created_at`, `updated_at` |
| `organizations` | `id`, `name`, `description`, `created_at`, `updated_at` |
| `contacts` | all fields |
| `contact_groups` | all fields |

> Need a new table? Ask the backend team to add it to `ALLOWED_TABLES` in `data_ref.py`. It's a one-liner.

---

## Filter Syntax

Filters are **equality only** — `{ field: value }`. All conditions are ANDed.

```typescript
// status = "incomplete" AND project_id = "..."
filter: { status: "incomplete", project_id: projectId }
```

For more complex queries (IN, range, full-text), use a dedicated API endpoint or tool call instead.

---

## Relationship to Bookmarks

This is **not** the same as user data bookmarks (`full_table`, `table_row`, etc.).

| | DataRef | Bookmark |
|---|---|---|
| **What it targets** | System DB tables (notes, tasks, projects…) | User-owned custom tables & lists |
| **Who creates it** | React devs (in code) | End users (from UI) |
| **Use case** | Attach app data to AI context | User copies their own data to clipboard |

Both end up as XML in the model's context. They serve different attachment flows.

---

## TypeScript File

Copy `data_ref_utils.ts` to your project's utils directory and import from there.

The file is standalone — no runtime dependencies beyond TypeScript types.
