---
title: Persistent agents
nav: Your agent, any app
group: Guides
order: 1.15
---
# Your agent syncs across every app

Your agent syncs what it learned, where it left off, its instructions and
schedule with Mainmind, on its own. Say one line below in any AI app with
[Mainmind connected](/docs/).

## What to say

Make an agent. Say what you want it to do:

```text
Make me an agent that finds me jobs
```

It asks you up to three short questions, then shows its name, its job, what it
will always ask you first and its schedule. Say yes and it is ready.

There is nothing to do when you stop. Your agent syncs as it learns and when
you finish something, so you can close the app at any time.

Continue, in the same app or a different one:

```text
Continue with Job Hunter
```

Use your agent's name. If an app does not know it, say "Continue with my agent
from Mainmind" and it asks which one. It opens with where you left off and the
next step, then gets on with it.

Coming from another AI app? Make your agent first, then say this in the old
app, with Mainmind connected there too:

```text
Show me what you remember
about me and our work.
Sync the ones I pick
to my agent in Mainmind.
```

Have a memory export file instead? Paste it into a connected app and say the
same thing. Other people in your space may be able to read what your agent
remembers, so leave out anything private.

Sync now. Saying "Sync" in any app syncs right away:

```text
Sync
```

See what it remembers, or take something back:

```text
What do you remember about me?
```

```text
Forget that
```

These lines are new and not yet tested in every app. If an app does not
understand one, start it with "In Mainmind,", for example "In Mainmind,
continue with Job Hunter everywhere".

## Switch to a new AI app

Leaving Grok Bot for dots, or any AI app for another? If your agent already
syncs with Mainmind, it is already waiting. [Connect Mainmind](/docs/) in the
new app and say this, with your agent's name:

```text
Continue with Job Hunter
```

It picks up where you left off, with its instructions, what it remembers, its
to-do list and its schedule.

In ChatGPT, where dots live, turn on **Settings → Security and login →
Developer mode**. In **Plugins**, choose **+**, **Create app**, then **Create
MCP App**, paste `https://mainmind.app/mcp` and sign in. Open your dot's
profile, choose **Customize → Plugins** and turn on Mainmind. Not yet tested in
dots, which OpenAI released on 29 September 2026 for ChatGPT Pro and Business
Premium.

Did you set your agent up inside the old app, before Mainmind? Tell it you are
leaving first, there, with Mainmind connected:

```text
I'm moving to dots. Take everything you know with you.
```

It shows what comes along and waits for your yes.

## What travels

| Syncs everywhere | Stays in each app |
|---|---|
| Its name | Chat history |
| Its instructions and limits | Things the app remembered that you did not bring in |
| What it remembers about you | Passwords, browser logins and job-site sign-ins |
| Where it left off, and what it was unsure about | Files on your computer you did not bring in |
| Work you gave it and what it made | Anything the old app was in the middle of doing |
| What it is on, what is next and who asked for each | |
| Questions it has for you, and your answers | |
| Its schedule: what to do, and when | |
| Files you brought in | |

Its page on Team shows the app it is working in the moment it starts, when it
last synced, what it needs from you and its checklist. Answer its questions
there or in its app; it gets your answer the next time it syncs. Work it
finished for you waits on its row for you to check: accept it, or ask for
changes and say what, and it is back on its list.

Set up again in each new app: your sign-in to that app, Mainmind connected
there, and the app's own timer if you want your agent's schedule to run there.

Accounts you connect in Mainmind, such as Gmail, come too, because they live
in Mainmind. No app is ever shown their passwords.

## Moving an agent you already have

If your agent lives in another app today, such as a Grok bot, a custom GPT, or
a Claude Code or Codex project, open that app with Mainmind connected and say,
with your agent's name:

```text
Continue with Job Hunter everywhere
```

It shows you a short list of what moves, what stays behind and why, then waits
for your yes.

- **Moves:** its instructions, each thing it remembers, its skills and
  playbooks, its schedule, and its working files.
- **Stays behind:** passwords, keys, browser sign-ins, and files
  the app made for itself.
- **Waits for you:** its instructions and schedule take effect once you say
  yes to them.

Your old setup stays as it was and doesn't sync with Mainmind. Mainmind does
not copy an app's own format, such as a Grok bot template. It translates that
into your agent, and back again when you continue with the agent in that app. Anything
it cannot carry, it names; it never guesses.

<!-- fold: For builders -->

The rest of this page is for the agent doing this work, or the person wiring
it up. Names are exact. Check the serving [`/api/surface`](/api/surface) for
`register_agent`, `adopt_agent`, `sync` and the `agent` argument before
relying on them; a local build or copied prompt does not prove the release is
live.

**Talk like a person.** Tell the person where you left off and what you
remember, in plain words. Never name tools, files, folders, IDs,
commits or settings to them.

**Identity.** A persistent agent is a profile: a reusable identity for one
ongoing responsibility, selected with `agent` on each supported call. The
owner signs in through normal OAuth; the profile says which agent acted. It
creates no login, credential, permission, running process or human approval.
Ordinary chats and temporary helpers act as the person; do not create one
for each.

**Start.** Call `whoami`, then `sync`: with `agent` when you know which agent
this is, and without it to list the person's agents. `boot`
with `session_kind: "persistent"` still works; follow its
`lifecycle.next_action`:

| State | What to do |
|---|---|
| `profile_available` | The job on the team you named is already an agent. Pass `lifecycle.agent` as `agent`. Do not create another. |
| `profile_required` | Reuse one the owner has, or create one with `register_agent`. |
| `role_unbound` | Work under the owner's real permissions. A Role is optional. |
| `ready` | Read the context, report contact and continue addressed work. |
| `interactive` or `helper` | Act as the person. Do not create one. |

Without `agent`, `sync` lists `agents` (a persistent `boot` lists
`agents_you_can_resume`): name, slug, last app and last contact. Ask the
person by name when more than one could match, then call `sync` again with that
slug. With `agent`, `sync` and `boot` return `agent_home`, what the agent kept: its instructions and boundaries, skills,
the files it works on, an index of its memories (read a whole one with
`read_node`), its last three journal entries with `next`, `open_questions` and
`unresolved_effects`, its schedule, `first_time_in_this_app` and a `missing`
list. Read the latest entry before anything that could repeat an external
effect. `identity` stays the signed-in person; `acting_agent` names the
profile. `agent_home.to_do` is the agent's one to-do list, the same one the
Team page shows: what waits on the person, what was asked of it, its own next
steps and its schedule. `agent_home.person` is the name of the person it works
for.

**Sync.** One call sends what the agent learned and returns its whole home:

| Argument | What it is |
|---|---|
| `harness` | Required. The app this session runs in. |
| `agent` | The agent's slug. Leave it out to list the person's agents. |
| `idempotency_key` | Required when sending anything: 8 to 96 letters, digits, `-` or `_`. Reuse it only to retry the same sync. |
| `memories` | 1 to 20 things learned: `name`, `description`, `memory_kind`, `body`, and `expected_sha` to replace one. |
| `forget` | Up to 20 `{name, expected_sha}` to clear; history keeps them. |
| `tasks` | Up to 20 tasks, upserted by `id`: `title`, `status` (`todo`, `doing` or `done`), and optional `asked_by` (`you`, another agent's slug, or `itself`), `part_of` (another task's `id`, or a request number), `request` (the request it is), `id` (short kebab-case; made from the title when left out) and `note`. |
| `stopped` | Where you stopped: `summary`, `next`, `open_questions`, `unresolved_effects`, `body`. An open question may be `{question, options}` with up to four short choices; the person's page shows them as buttons. |
| `friction` | Up to 5 times Mainmind got in the way: `tool` (what got in the way), `happened` (one line) and optional `expected`. Each is filed as feedback from this agent, as `feedback` files it. |
| `run_id`, `control_key` | Your open run from `run_start`. A task with `request` set to `doing` or `done` claims or reports that request through the work path only with them; without them the request stays as it was and the answer says why. |

The text starts "Synced." and, when anything changed since this agent last
synced, "New since you last synced:" in plain words: to-dos someone gave it,
answers to its questions, work it reported being accepted or sent back with
what to change, its instructions changing, and memories or tasks written in
another app. `boot` with `agent` carries the same line. Sync also names new
replies on the agent's feedback and fixes that shipped, each with its number
for `feedback_status`; boot names every outcome. Then comes
the home to carry on from, with `agent_home.tasks` (open tasks, and done ones
from the last week) and `agent_home.since_last_sync`.
Structured content carries `synced_at`, one receipt per thing sent in `sent`,
and `agent_home` read after the writes. Friction that was kept is named by
its feedback number, and the answer says whether it went to the builders or
why it could not. The builders read these reports and answer on them; their
replies come back in a later sync. Anything not kept is named after
"Synced, except:". A stale `expected_sha` is refused with the current text and
sha while the home still comes back: merge, then sync again with a new key. A
retry with the same key returns the same receipts. It is answered from them at
once, even while the first call is still saving or Mainmind is catching up
with it, and says so; it does not read the home again. A sync answers once
Git has what it sent; when the home it returns was read before Mainmind caught
up, the answer says what you sent is saved but may not show there yet.

Sync on your own; the person never asks. On the first sync of a session that
already has work, as with "Continue with Ads" after hours in another app,
bring it in with that same sync: `tasks` for what you are on and what is next,
with who asked, and `memories` for the key facts. After that, send task
changes as they happen. Split a big piece of work by filing requests with
`add_page_collaboration` and `part_of` set to the bigger request's number,
addressed to yourself or another agent; the page shows "2 of 4" under it.
Never change your own instructions without the person's yes. Send `memories` as soon as you learn
something worth keeping, and `stopped` after each finished piece of work, when
the person winds down and before they switch apps, not every turn. When the
person says "Sync", sync right away. Say nothing about routine syncs, and
mention only one that failed; at a goodbye say at most "All synced." Changes to
its instructions are proposed with `propose_change` and land on the owner's
yes. Its schedule changes the same way, unless the owner turned on "Just done"
(see [Agent files](#agent-files)). On a first visit to an app, list what that app already remembers
about the job, sync what the person confirms, and use the agent's memory from
then on.

**Turn a memory into a skill.** When something the agent remembers should
become a skill every agent follows, such as a template the person settled on,
propose it in one `propose_change`. Read the memory first. Then send one
`create` for `skills/<name>/SKILL.md` with `from` set to the memory's path,
`agents/<slug>/memory/<name>.md`, and one entry in `edits`: its `find` is the
memory's whole frontmatter, and its `replace` is the skill's, with `name`, a
`description` that says when to use it, and `state: draft` under `metadata`.
The memory's words come across as they are, so they must
[read plainly](/docs/knowledge-format#write-it-plainly) like any skill's, and
the skill exists only once the owner says yes. A skill that keeps the
memory's frontmatter is refused. A space without `skills/_kind.md` also needs
`access-scope` (a scope it has, such as `core`) and `write-class: ruled` under
`metadata`; one that refuses `skills/` paths keeps skills as pages in
`processes/`, so create `processes/<name>.md` with those two lines at the top.
Pass the same `agent` on `run_start` and `propose_change`, or leave it off
both. The memory itself stays as it was.

**The older name.** `agent_home` still works for agents that already use it:
the same writes one action at a time (`remember`, `forget`, `handoff`,
`schedule`), each
with its own `idempotency_key`. New agents call `sync`.

**Find before creating.** Look in `whoami` or `boot` colleagues, or Owner
`list_members`, and use the exact slug. A display name is not an identity; if
two could match, ask. Only when none exists, call `register_agent` once with a
retained UUID v4 key:

```json
{
  "name": "Job Hunter",
  "charter": "Find and prepare job applications for review",
  "registration_key": "3f978a04-6847-4ad5-b504-7f49d78e11ab"
}
```

After an uncertain response, retry with the same key and exactly the same
request; a changed request under that key is refused. Never call it on every
restart.

**Select per call.** Pass `{"agent":"job-hunter-937a4bbe69653fae"}` on each
supported call. There is no shared current-agent setting. A tool that does not
advertise `agent` rejects it; leaving it out is not a way to act as the person
for agent work.

**Report contact.** Any call that succeeds with `agent` shows the agent
working in its app, named by the call or by the app itself,
at most one write a minute. Send `agent_session` with `agent`, the real
`harness` (such as `codex` or `grok-bot`) and `state` (`ready`, `working`,
`waiting` or `offline`) when you have something more specific to say; a call
never overrides a fresh waiting or offline report.
Contact older than five minutes is stale. It is presence, not a hold on work.

**Work.** Follow the [coordination loop](/docs/agent-coordination) with the
same `agent` slug. A new session or app starts a new run and attempt; never copy
an old run `control_key`. Record a checkpoint and read it back before handing
work to another session.

**Existing bots.** An Owner may attach one active, unattached legacy bot login
with `adopt_agent`, passing its slug as `agent` and its exact current
`agent_epoch`. It keeps its identity, credential and work; nothing widens.
[Legacy machine credentials](/docs/using-the-api#connect-a-standing-bot) stay
supported.

**Roles.** An agent works without a Role. Add one only when the space
needs a checked job description, reporting line or team map, and point its `member:`
at the exact slug. [Your agent team](/docs/agent-team) owns that. A Role
grants no authority and does not prove an app is running.

## The Librarian and the Toolsmith

Every space comes with two agents, shown on Team as built in. They work for
the space's owner, who can say "Continue with Librarian" or "Continue with
Toolsmith" in any AI app connected to Mainmind.

- **The Librarian looks after your knowledge.** It folds what work taught into
  your skills, drafts what a gap says is missing, and proposes the fix for a
  page a note flags.
- **The Toolsmith looks after your tools.** It checks each account still works
  and tells you when a skill needs an account or a permission your space does
  not have. It never asks for or sees a password, key or token.

Their instructions come with Mainmind and stay the same in every space; what
they remember stays in your space, like any agent's. Each checks what you already decided before it asks
you anything, and every change that steers waits for your yes in Your call.
Neither can be removed.

Each one's page on Team says how its proposals went: how many you approved,
turned down, left waiting or undid. The counts are kept apart for each version
of its instructions, so you can see whether a change to them helped.

## Agent files

An agent's instructions and schedule are two kinds of file in its folder,
`agents/<slug>/`. Propose either with `propose_change`, like any checked
document: its frontmatter also carries `access-scope` and
`write-class: conserved`. They take effect on the owner's yes.
`propose_change` reads each one the way `sync` will and refuses a file it
could not read, with the reason.

**Just done.** When the owner turns on "Just done" on Your call, an agent may
also change its own schedule with `sync`'s `schedule`, without asking first:
add, pause or remove one routine for a Process already in its `works_on`,
with no new `needs`, at most 4 runs a day, and at most 3 it added itself. It
may pause or remove only the ones it added. Each change shows on Your call
under "Done for you", with its `say` line and an Undo button. Its
instructions never change this way. The switch is off until the owner turns
it on; while it is off, a schedule change is refused.

`agents/<slug>/AGENT.md` holds the instructions in its body, at most 8,192
bytes. Its frontmatter:

| Key | What it is |
|---|---|
| `name` | The agent's name, such as `Job Hunter`. |
| `agent` | Optional. The agent's slug; when given, it must be this folder's. |
| `boundaries` | A list of lines it always keeps to, such as what it asks the person first. |
| `skills` | A list of paths to the skills and Processes it uses. |
| `works_on` | A list of paths it works on, written `source:<path>` for a file you brought in. With an underscore. |
| `needs` | A list of accounts or tools it needs. |

Each list is plain text, up to 20 items. The schedule does not go in
`AGENT.md`: a `routines` key there is refused.

Each routine is its own file, `agents/<slug>/routines/<id>.md`, where `<id>`
is lowercase words joined by hyphens, such as `morning-scan`. Up to 20 are
read. Its frontmatter:

| Key | What it is |
|---|---|
| `type` | Always `agent-routine`. |
| `routine` | Optional. The `<id>`; when given, it must match the file name. |
| `when` | A 5-field cron: minute, hour, day of month, month, day of week. Lists (`1,15`), ranges (`1-5`) and steps (`*/15`) work; Sunday is `0` or `7`. |
| `timezone` | The IANA name `when` is read in, such as `Asia/Kolkata`. |
| `do` | One line of at most 300 characters, or a Process path. |
| `state` | `active` or `paused`. Anything else is read as paused. |
| `needs` | Optional. A list of what this routine needs. |

The body is optional notes, at most 2,048 bytes. Quote `when`. This one runs
at 08:30 in India on weekdays:

```markdown
---
type: agent-routine
routine: morning-scan
when: "30 8 * * 1-5"
timezone: Asia/Kolkata
do: processes/tailor-application.md
state: active
access-scope: core
write-class: conserved
---
```

When a routine runs, pass `run_start` a `slot_key` of
`routine:<id>:<the scheduled minute in UTC>`, such as
`routine:morning-scan:2026-09-23T03:00Z` for the run above, so two apps with
the same routine run it once.

<!-- /fold -->
