# See what your agents did

> Read the record of every call an agent made to Anectico, with its outcome, and see which proof pages people opened: what is recorded, what is never recorded, who can see it and how long it is kept.

Canonical page: https://anectico.com/docs/manage/agent-activity/


You hand real work to your agent. This page is about the record that shows what each agent did
with Anectico: every call, reads included, and how each call ended.

It is a different record from the [audit log](/docs/manage/console). The audit log keeps security
events and changes that were applied: a key was created, a role changed, a deletion ran. The
activity record keeps every call an agent made, including the calls that only read and the calls
that were refused.

It is also different from [agent runs](/docs/investigate/agent-runs). Agent runs describe the AI
agents inside your own product, seen through the telemetry you send. The activity record is about
agents calling Anectico.

## What an agent is here

An agent is a credential:

- an API key that reads or manages a project, or
- a connection that a person approved for an agent host.

One row belongs to one credential. That is the thing you can revoke, so that is what the record
groups by. If a key was created for a registered agent, the row also carries that agent's id.

## What is recorded

One row is written for every call an agent makes:

| Field | What it holds |
| --- | --- |
| `occurred_at` | When the call started. |
| `agent` | The key or the connection, its current name, and the person it belongs to. |
| `surface` | `mcp`, `cli` or `rest`. |
| `operation` | The MCP tool or action name, or the method and route pattern, for example `GET /api/v1/issues/{id}`. |
| `class` | `read`, `write`, `destructive` or `export`. |
| `outcome` | How the call ended. See the next table. |
| `refusal` | Why, when the outcome is `refused`. |
| `duration_ms` | How long the call took. |
| `project_id` | The project the call addressed. Empty when the call addressed the whole workspace. |
| `objects` | The kind and id of up to eight things the call named, for example an incident or a dashboard. |
| `missing_scopes` | For a call refused because the key lacked a permission: the names of the permissions it needed. See [A refused call says what it needed](#a-refused-call-says-what-it-needed). |
| `skills`, `skill_basis` | Only on a skill report: the skills the Anectico CLI installed, upgraded or removed. See [Skill reports](#skill-reports). |

The outcome is one of these words:

| Outcome | Meaning |
| --- | --- |
| `ok` | Answered. For a change: it was applied. |
| `previewed` | A change that returned a preview and changed nothing. |
| `partial` | Answered or applied in part. |
| `refused` | Anectico declined the call. `refusal` says why. |
| `error` | Anectico failed to answer. |
| `canceled` | The agent went away before the answer. |
| `unknown` | A change that started. Its end was not recorded. |

A refusal has one of these reasons: `permission`, `plan`, `validation`, `not_found`, `conflict`,
`rate_limited`, `not_ready` or `budget`. A budget refusal states the operation class, the
limit and its UTC reset time. Ask the workspace owner to change the allowance; do not retry in a loop.

An outcome of `unknown` does not mean that nothing happened. Check the object the call named.

## What is never recorded

The record is narrow on purpose.

- **No request or result data.** No query text, no filter text, no message, no property value and
  no free text that the agent sent.
- **No raw address.** A route is stored as its pattern. The ids in the address are not part of it.
- **No person and no account.** An id of a tracked person or an account is never stored. The row
  counts such an object in `objects_omitted`. So erasing a person changes nothing in this record,
  and a person's export does not include it.
- **No secret.** No key value, no token, and no Live viewing link.
- **No Console page load.** What a signed-in person does in the Console is not agent activity, and
  it is not in this list. Only calls made with a key or an approved connection are recorded. The one
  thing the Console reports is that a proof page opened. That is a separate record of what people
  looked at, described in [Which proof pages people opened](#which-proof-pages-people-opened).
- **No runtime call from your own application.** A flag decision, a survey answer or a browser
  recording that your application sends with its application key is not agent activity. The same
  call made with an agent's key is recorded.
- **No Live display.** A screen that shows a published Live link is not recorded. Creating,
  listing, pausing and revoking those links are agent calls and are recorded.

A call that cannot sign in is not recorded, because there is no credential to record it for.

## Changes and exports are recorded first

For a call that can change something, Anectico writes the row before it runs the call. If the row
cannot be written, the call is refused and nothing changes. Over MCP the agent reads "this call
was not run and nothing changed". Over the API it gets status `503` with the error
`activity_record_unavailable`. Retry it.

An export is treated the same way, because data that has left cannot be taken back. This covers
asking for an export, running one again, a scheduled delivery to your own storage, and fetching
the result: the download address and the file itself. The request that creates an export and each
request that fetches its result are separate rows. An export that was cut off after it started has
the outcome `partial`.

The live log tail is a read, but its row is also written before the first line is sent.

A call that only reads is answered first and recorded after. If that row cannot be written, the
read is still answered. The next row of the same agent then carries `unrecorded_reads_before`: the
number of read calls that happened and are not in the list, and `unrecorded_since`. The summary
shows the same count as `unrecorded_reads`. These calls are never added into `calls`.

## Who can see it

- **Owners and admins** see every agent in the workspace.
- **Everyone else** sees the activity of their own agents: the keys they created and the
  connections they approved.
- **An agent** sees only its own calls. A key or a connected agent does not see the other keys of
  the person who created it.

The permission is `activity:read`. Every role has it. Seeing the whole workspace also needs
`audit:read`, which only owners and admins have. An owner or admin can give a key `audit:read`. A
connected agent can never have it. Each answer says which of the two you got, in `visibility`:
`own` or `workspace`.

A key that is limited to one project sees only calls in that project, and the summary counts only
those calls.

A filter never shows more. If you ask for a key that you may not see, you get the same empty list
as for a key that does not exist.

### Objects you may not read

A row names the objects of a call only to a reader who may read that kind of object. For example,
you see the id of an incident only if you have `incidents:read`. Other objects are counted in
`objects_withheld`, and `withheld_object_scopes` lists the permissions that would show them. You
also cannot filter the list by an object of a kind you may not read.

## A refused call says what it needed

When Anectico refuses a call because the key lacks a permission, the row lists the missing
permissions in `missing_scopes`, sorted. Read it to learn which key the automation needs. For
example, a call to read issues with a key that has no `errors:read` shows:

```json
{ "outcome": "refused", "refusal": "permission", "missing_scopes": ["errors:read"] }
```

Then create a key that has the permission, for example with `anectico apikey create --scope errors:read`.
See [Permission scopes](/docs/reference/permissions).

How to read the list:

- An empty list means the refusal did not name a permission. This happens when a confirmation did
  not match, or when the key is not allowed to use the project.
- A call can need more than one permission. The list holds up to eight names.
- Over MCP, an action that the key cannot list at all is refused without a name. Anectico does not
  tell a key what it lacks for an action it cannot see. Run `anectico whoami` to see what the key holds.
- The names are Anectico's own permission names. A name that Anectico does not know is never stored.

## Skill reports

A skill is a text file that your agent reads. Anectico cannot see which skill an agent follows, and
it does not guess. What the CLI can tell is when it changes skill files.

When you run `anectico skill install`, `anectico skill upgrade` or `anectico skill remove` with an
API key, the CLI sends one short report: what it did, and the names of the skills it changed. It
sends no path, no host name, no user name and no machine name. Anectico records it as a call of
that key. A report shows what is installed. It does not show what an agent used.

```bash
anectico activity list --skill anectico-triage-issue
anectico activity summary --since 30d
```

The list shows the report rows. The summary has a `skills` list: for each skill and action, the
number of reports, the number of keys that made them, and the last time. Each answer repeats that a
skill report is not skill use.

- **Skill use over MCP is not measured.** A tool call carries no skill.
- The CLI sends no report when there is no API key, for example after `anectico login`.
- To turn the report off, set `ANECTICO_SKILL_REPORT=off`. If a report fails or is slow, the command
  prints what it always prints and exits as it always does. It waits at most three seconds.

## Which proof pages people opened

An agent ends an answer with proof links. See [Proof pages](/docs/agents/proof-pages). Anectico
records whether people open them, so you can ask which links were opened, of which kinds, and how
many of the objects your agents gave were never opened.

An **open** is recorded when a signed-in person opens a proof page in the Console, in a project
that person can open. The Console reports it once for each page load. It makes one request with the
person's own sign-in. It sets no cookie and calls no other service.

This is a record of what the people in your team looked at, so read it as that. It is kept apart
from the record of what agents did.

| What is recorded | Detail |
| --- | --- |
| The kind of page | Issue, trace, agent run, incident, dashboard, person, session replay or result. |
| The project | The project in the link. |
| The object | The id in the link, for an issue, trace, agent run, incident, dashboard or stored result. |
| Who | The signed-in team member. Anectico reads this from the sign-in, not from the page. |
| When | Anectico's own clock. |

- **A person or a session replay is counted by kind only.** The id of a person and the id of a
  session are never stored here. An open of those pages is in the count of its kind and in no
  object.
- **The same person opening the same object again in the same ten-minute period counts once.** One person can add
  at most 120 opens in an hour.
- **Rows are kept 30 days.** They are deleted with their project or workspace. When a member leaves the
  workspace, their name is removed from their opens. The counts stay.
- **Counts name nobody.** Everyone with `activity:read` sees the counts and, for each kind of object
  they may read, the ids. Only a reader who also has `audit:read` (owners and admins) sees who opened.
- **No analytics service, no cookie, no fingerprint.** Nothing about the browser is sent.

### Ask which links were opened

> Which proof links did my team open this week, and how many that my agents gave were never opened?

```bash
anectico activity opens --since 7d
anectico activity opens --since 30d --project <project-id> --top 5
```

The answer has the opens by kind and by day, the most opened objects, and `join`: how many of the
objects your agents gave were opened afterwards, and how many were not. `join.rule` states how an
open is matched. Read the rule before you say a number:

- An object is **given** when an answered MCP call by an agent you can see returned it in its result,
  in a project. The CLI and the API hand out no link, so only MCP calls count. An id that the agent
  only typed into a call does not count, and neither does an object that a call deleted.
- It is **opened** when a person opened its proof page, in the same project, after the first such call.
- Anectico stores the objects a result returned, at most eight for each call, and not the links. An
  object that was returned and not passed on counts as given, and a call that returned more than eight
  objects is counted for eight. The figures count objects. They are not a count of links that people
  followed.
- An open is recorded for the address that was opened. Anectico does not check that the object exists. An
  open of an id that does not exist can never match, because only an object that a result returned counts
  as given.
- A call that addressed no project cannot be matched. `given_without_project` counts those objects.
- `opened_not_given` counts objects that were opened and match no given object. A person may have
  reached the page another way, or an agent you cannot see may have given it.
- With `activity:read` alone, `join` covers your own agents. With `audit:read` as well, it covers every
  agent in the workspace.

What the answer cannot see is in `not_measured`: a link opened outside the Console, such as a link
pasted where the person was signed out; whether the person read the page; and the pages of people and
session replays, which cannot be matched to an object. A recorded open does not show that the object
was found.

## How long it is kept

A row is kept for 30 days and then deleted. Nothing older can be read. Deleting a project deletes
its rows, and deleting the workspace deletes all of them. The opens of proof pages follow the same
rules.

## In the Console

**Home** shows **Recent agent activity** when an agent is connected: each agent that made calls in
the last 24 hours, its counts for that time, and its latest calls with their outcome. Home shows
calls in the selected project, and calls that addressed no project.

**Agents and keys** has the full list under **Agent activity**, newest first.

## Ask your agent

Your agent can read the record itself. This is how it checks its own earlier steps, and how you
ask it what another agent did.

> What did my agents do in Anectico in the last day? Show me anything that was refused.

| Job | MCP action | CLI command |
| --- | --- | --- |
| List calls, newest first | `list_agent_activity` | `anectico activity list` |
| Count calls, refusals and errors per agent | `summarize_agent_activity` | `anectico activity summary` |
| See which proof pages people opened, and which given objects never were | `summarize_proof_link_opens` | `anectico activity opens` |

```bash
anectico activity summary --since 7d
anectico activity list --since 24h --outcome refused
anectico activity list --class destructive --credential <key-or-connection-id>
anectico activity list --object-kind dashboard --object-id <id>
```

All three MCP actions run through `execute_read_action`.

How to read the answer:

- The answer states its window, and whose activity it covers.
- A list is one page. `next_page_token` means there are more calls. Do not report a page as a
  total. Use the summary for counts; it counts stored rows.
- You can filter by time, agent, person, outcome, class, surface, operation and object.
- You cannot search for a person or an account, because their ids are not stored. The call is
  refused instead of returning an empty list.
- Report `unrecorded_reads_before` when a row has it. It is a known gap.
- When a call was refused, report `missing_scopes`. It is the permission the key needs.
- A skill report is not skill use. Say "installed", never "used".
- For opens, report the rule with the numbers, and say that they count objects and not links followed.

## Where to go next

- [The Console](/docs/manage/console): the pages that control access.
- [Revoke an agent's access](/docs/agents/revoke-agent-access): stop a key or a connection.
- [Permission scopes](/docs/reference/permissions): `activity:read` and `audit:read`.
