# Meta
URL: /tap/docs/store/meta

Track scope origin with source and query.

> For AI agents: a documentation index is available at [llms.txt](/llms.txt). Use `.md` for canonical markdown pages; `.mdx` is kept as a backwards-compatible alias on supported URL paths.

Every scope accessor has three metadata properties: `source` and `query` describe where a scope comes from, and `name` is the scope's own name. They are reserved — scope methods cannot use these names.

```
aui.thread.source; // "root" | "parentScope" | null
aui.thread.query; // {} | { index: 0 } | null
aui.thread.name; // "thread"
```

## Root scopes

When you fill a scope directly via a provider's `AuiConfig({ ... })`, it gets `source: "root"` and an empty `query`:

```
const config = AuiConfig({ thread: ThreadResource() });

<AuiProvider config={config}>

aui.thread.source; // "root"
aui.thread.query; // {}
```

This is the default — the scope was provided directly, not derived from another scope.

## Derived scopes

When a scope is created via `Derived`, its `source` points to the parent scope it was derived from, and `query` carries the lookup parameters:

```
const aui = useAui();
const config = AuiConfig({
  message: Derived({
    source: "thread",
    query: { index: 0 },
    get: (aui) => aui.thread.message({ index: 0 }),
  }),
});

<AuiProvider extends={aui} config={config}>

aui.message.source; // "thread"
aui.message.query; // { index: 0 }
```

You can declare the expected meta shape in `ScopeRegistry`:

```
declare module "@assistant-ui/store" {
  interface ScopeRegistry {
    message: {
      methods: {
        getState: () => { role: string; content: string };
      };
      meta: { source: "thread"; query: { index: number } };
    };
  }
}
```

This makes the `source` and `query` types precise — TypeScript will enforce that any `Derived` providing the `message` scope uses the correct source and query shape.

## Unavailable scopes

When a scope hasn't been provided by any `AuiProvider` above the current component, its accessor has `source: null` and `query: null`. `name` still answers; calling the accessor or reading any other property throws:

```
aui.message.source; // null — no message scope in context
aui.message.query; // null
aui.message.name; // "message"
aui.message.getState(); // throws
```

The accessor itself is always truthy, so `if (aui.message)` does not tell you anything. Check availability through the `optional` view, which resolves an unavailable scope to `undefined`:

```
if (aui.optional.message) {
  // scope is available
}
```

## Summary

| `source`        | Meaning                                            |
| --------------- | -------------------------------------------------- |
| `"root"`        | Scope was filled directly via `AuiConfig({ ... })` |
| `"parentScope"` | Scope was derived from another scope via `Derived` |
| `null`          | Scope is not available in the current context      |