maestro/docs/user-folder-layout.md
2026-06-03 05:08:00 +00:00

200 lines
8.2 KiB
Markdown

# User Folder Layout
## Overview
Every authenticated user gets a personal folder at `data/users/{userId}/`.
This folder is the user's private, cross-task workspace. Unlike a job's
ephemeral workspace (which lives under `{worktree_dir}/local/{taskId}/` and is
tied to a single run), the user folder **persists indefinitely** across tasks,
sessions, and server restarts.
The primary use-cases are:
- Storing reusable scripts (`scripts/`) and browser macros (`browser-macros/`) that any of your tasks can invoke via `RunUserScript`.
- Keeping template files and reference documents you want agents to access without uploading them every time.
- Holding auto-generated recordings of browser sessions so you can review or convert them later.
- Managing saved browser login sessions (`browser-sessions/`) that macros can use.
Access is **owner-only**: the REST API enforces that only the owner (or an
admin) can read, write, or delete files inside the folder. The directory is
created on first login and is never shared between accounts.
---
## Subdirectories
### `scripts/`
**AI-generated plain Node.js programs.** No Chromium. Signature: `main({ params })`.
Best for: data processing, API calls, computation, file conversion, scheduled task helpers — anything that does not need a browser.
Files are edited directly in the **User Folder → scripts/** panel. The agent writes and runs these via `RunUserScript({ name, kind: 'script' })` (the default `kind`).
See [docs/tools/runuserscript.md](tools/runuserscript.md) for the exact file format and invocation details.
### `browser-macros/`
**Playwright-based browser automation scripts.** Launches Chromium. Signature: `main({ context, params })`.
Generated automatically by the **Save as Script** button in the recordings panel (previously these went to `scripts/`). Can also be written manually in the UI. The agent runs them via `RunUserScript({ name, kind: 'browser-macro' })`.
If a `session_profile_id` is declared in the frontmatter, the corresponding saved browser session (from `browser-sessions/`) is loaded automatically.
**Self-healing patches**: when a macro fails, the agent auto-enables the BrowseWeb recorder; on task completion a candidate patch is staged as `browser-macros/{name}.next.js`. The Diff review pane lets you accept or reject it. See [Self-Healing Patches](#self-healing-script-patches) below.
### `templates/`
Static files — Markdown snippets, HTML skeletons, CSV headers, prompt
fragments — that you want to reuse across tasks. Agents can read these with
the standard `Read` tool by referencing the path the API returns.
### `recordings/`
Browser-session recordings produced by `BrowseWeb` when the `record_to`
parameter is set. Each recording lands here as `{name}.json` once the session
ends. The file contains an ordered list of timestamped actions.
You never write here directly — the server writes recordings automatically.
The **User Folder → recordings/** panel lets you view recordings and convert them
to browser macros via **Save as Script** (saves to `browser-macros/`).
### `browser-sessions/`
**Virtual subdir (no actual filesystem directory).** Manages saved browser login
profiles — cookies and storage state captured via noVNC (CAPTCHA/2FA bypass).
Sessions are encrypted with a per-user key.
Managed from the **User Folder → browser-sessions/** panel in the UI:
1. Click "Add site session", enter the URL and a label.
2. Log in inside the noVNC window that opens.
3. Click Save — the encrypted storage state is written to the DB.
Browser macros reference a session by `session_profile_id: <N>` in their frontmatter.
### `trash/`
Soft-deleted scripts, macros, templates, and recordings are moved here rather than
immediately erased. Files in `trash/` accumulate with a timestamp prefix:
`{YYYYMMDD-HHMMSS}-{rand4hex}-{name}`. Restore by copying the content back to the
original subdir via the editor.
### `memory/`
Persistent memory entries managed by the `UpdateUserMemory` and `ReadUserMemory` tools.
**`MEMORY.md`** (index file) — automatically injected into the agent's system prompt at the start of every movement. It contains one line per entry:
```
- [preferred-language](preferred-language.md) — User prefers Japanese output
- [project-stack](project-stack.md) — Tech stack for the main project
```
**Individual fact files** (`{name}.md`) — each has YAML frontmatter followed by a plain Markdown body:
```
---
name: preferred-language
description: User prefers Japanese output
type: user
---
Always respond in Japanese unless the user explicitly asks for another language.
```
**Note:** `MEMORY.md` is agent-managed via `UpdateUserMemory`. Manual edits to the index file are tolerated but may cause duplicate lines if you change the link format `- [name](file.md)`.
**Memory types:**
| Type | Intended use |
|------|-------------|
| `user` | Long-term user preferences, standing instructions |
| `feedback` | Corrections the user has given (e.g. "don't do X") |
| `project` | Project-specific facts (stack, conventions, key files) |
| `reference` | Reference data (URLs, credentials patterns, external IDs) |
---
## AGENTS.md
`data/users/{userId}/AGENTS.md` (at the top level of your user folder, not
inside a subdirectory) is your **personal agent instruction file**. Whenever
the orchestrator starts a task on your behalf it reads this file and injects
its contents into the agent's system prompt, before any piece-specific
instructions.
Edit it from the **Settings → Agent instructions** panel or with any text
editor — the agent picks up the latest version at the start of each task.
---
## Script vs Browser-Macro Format
### Plain scripts (`scripts/`)
```js
---
description: "Fetch and summarise data"
params:
- name: url
type: string
---
async function main({ params }) {
const data = await fetch(params.url).then(r => r.json());
return data.summary;
}
module.exports = main;
```
Invocation: `RunUserScript({ name: 'my-script', kind: 'script', params: { url: '...' } })`
### Browser macros (`browser-macros/`)
```js
---
description: "Log in and navigate to the dashboard"
params:
- name: username
type: string
- name: password
type: string
session_profile_id: 1
---
async function main({ context, params }) {
const page = await context.newPage();
try {
await page.goto('https://example.com/login');
await page.locator('#username').fill(params.username);
await page.locator('#password').fill(params.password);
await page.locator('button[type=submit]').click();
} finally {
await page.close();
}
}
module.exports = main;
```
Invocation: `RunUserScript({ name: 'login', kind: 'browser-macro', params: { username: '...', password: '...' } })`
Full field reference, parameter passing, and session-profile selection are documented in [docs/tools/runuserscript.md](tools/runuserscript.md).
---
## Self-Healing Script Patches
When a `RunUserScript` call with `kind: 'browser-macro'` fails because a selector no longer matches the page, the agent automatically enables the BrowseWeb recorder. The recorded interactions are compiled into a candidate patch at `browser-macros/{name}.next.js`.
The Diff review pane in **User Folder → browser-macros/** compares the current `.js` with the `.next.js` candidate and asks you to approve or reject the patch. On approval the server atomically replaces `{name}.js`. On rejection the staging file is discarded.
This means the live `browser-macros/` folder always contains only reviewed, approved code.
---
## Permissions and Privacy
The `data/users/{userId}/` directory is created with mode `0700` (owner read/write/execute only) at the filesystem level. At the API level every endpoint under `/api/users/me/` requires a valid session cookie and verifies that the authenticated user's ID matches the folder being accessed. Admin users can access any user's folder.
No other user — including users in the same organisation — can list or read your scripts, macros, templates, recordings, or AGENTS.md.
### Auth-disabled mode (local dev only)
When the `auth:` section is absent from `config.yaml` (`authActive=false`), the User Folder API injects a synthetic `{ id: 'local', role: 'user' }` user, so all operations go to `data/users/local/`. Production deployments should always run with auth enabled.