sync: update from private repo (ce93095)
CI / build-and-test (push) Has been cancelled

This commit is contained in:
oss-sync
2026-06-10 03:52:37 +00:00
parent eb35e32f7a
commit 9f8958c4a2
27 changed files with 261 additions and 1582 deletions
-85
View File
@@ -1,85 +0,0 @@
# ReadUserTemplate
Loads a template file from the caller's `templates/` subdir (`data/users/{userId}/templates/`).
## Overview
Templates are plain Markdown files the user stores in their `templates/` folder via the UI.
Unlike `memory/` entries, frontmatter is **optional** — a template can be pure Markdown prose
with no YAML header at all.
Use this tool when the user says "use the weekly-report template" or "follow the api-error-email
boilerplate" — call `ReadUserTemplate`, read the shape, then adapt it to the task at hand.
---
## Usage
```json
{ "name": "weekly-report" }
```
Or with the `.md` extension (both forms work):
```json
{ "name": "weekly-report.md" }
```
**Response example (no frontmatter):**
```
# Template: weekly-report
## Body
# Weekly Report
Fill in this week's highlights here.
```
**Response example (with frontmatter):**
```
# Template: api-error-email
## Frontmatter
title: "API Error Email"
audience: "external"
## Body
Dear customer,
We apologize for the inconvenience.
```
---
## Parameters
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `name` | string | Yes | Template filename, with or without `.md` extension (max 128 chars) |
---
## Error cases
- Returns an error if the template does not exist in `templates/`.
- Returns an error if no user is authenticated.
- Returns an error if `name` contains path traversal characters or slashes.
---
## Use cases
- Weekly / monthly report boilerplate: `ReadUserTemplate({ name: "weekly-report" })` → fill in stats.
- Email canned responses: `ReadUserTemplate({ name: "api-error-email" })` → personalise and send.
- Code boilerplate: `ReadUserTemplate({ name: "react-component" })` → generate a new component.
---
## Related tools
- `ListUserAssets({ kind: "templates" })` — see what templates exist before reading one.
- `ReadUserMemory` — for structured facts/preferences (requires frontmatter).
- `RunUserScript` — for executable Node/Playwright scripts stored in `scripts/` / `browser-macros/`.
- This tool is a META_TOOL — no need to add it to `allowed_tools` in piece YAML.
-108
View File
@@ -1,108 +0,0 @@
# RenderUserTemplate
Renders a template from `templates/` by substituting `{{var}}` placeholders with caller-supplied params.
## Overview
Companion to `ReadUserTemplate`. Instead of returning the raw body, this tool:
1. Parses the template's frontmatter `params` spec (same shape as scripts / browser-macros).
2. Validates caller-supplied `params` against the spec (type-check + defaults applied).
3. Replaces every `{{name}}` placeholder in the body with the resolved value.
4. Returns the rendered body (no `# Template:` header, no frontmatter — just the substituted text).
Placeholders **not declared** in `frontmatter.params` are left literal — so prose like
`use {{column}} as the key` survives unchanged when no `column` param exists.
---
## Usage
Template file `templates/weekly-report.md`:
```markdown
---
description: Weekly status report
params:
- name: date
type: string
- name: summary
type: string
default: "(no summary)"
---
# Status — {{date}}
{{summary}}
```
Call:
```json
{ "name": "weekly-report", "params": { "date": "2026-05-11", "summary": "shipped 3 PRs" } }
```
Response:
```
# Status — 2026-05-11
shipped 3 PRs
```
---
## Parameters
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `name` | string | Yes | Template filename, with or without `.md` extension (max 128 chars) |
| `params` | object | No | Key-value params matching the template's `frontmatter.params` spec |
---
## Frontmatter `params` schema
Identical to scripts / browser-macros:
```yaml
params:
- name: identifier # must match /^[a-zA-Z_$][a-zA-Z0-9_$]*$/
type: string | number | boolean
description: optional
default: optional # if omitted, the param is required
```
Param values are coerced to string via `String(value)` when substituted.
---
## Error cases
- `name` missing or invalid characters → error.
- Template file does not exist in `templates/` → error.
- Frontmatter is malformed (bad YAML, bad `params` shape) → error.
- A required param (no default) is missing from the call → error: `param X: required but not provided`.
- A param has the wrong type → error: `param X: expected number, got string`.
Templates without any frontmatter render as pure pass-through — any `{{var}}` stays literal.
---
## Use cases
- Weekly / monthly reports: `RenderUserTemplate({ name: "weekly-report", params: { date, summary } })`.
- Email canned responses with variable substitution.
- Code boilerplate with a few configurable parts (component name, props).
For more dynamic logic (conditionals, loops), open `ReadUserTemplate` and do the substitution
inline in your output — there is no Handlebars / Liquid / etc. engine, by design.
---
## Related tools
- `ReadUserTemplate` — returns the raw body + frontmatter. Use this when you want to inspect
the template structure or do substitution yourself.
- `ListUserAssets({ kind: "templates" })` — list available templates.
- `RunUserScript` — for executable scripts (not just text substitution).
- This tool is a META_TOOL — no need to add it to `allowed_tools` in piece YAML.
+34 -52
View File
@@ -1,27 +1,29 @@
# RunUserScript
Executes a user-authored script from the caller's user folder.
Executes a user-authored Playwright browser-macro from the caller's
`browser-macros/` folder.
Two kinds of scripts are supported:
> **Retired (2026-06):** plain-Node `scripts/` (the old `kind: 'script'`) and
> `templates/` were removed. Keep reusable procedures/boilerplate in **Skills**,
> and run ad-hoc code (Node, Python, …) with the **Bash** tool. Passing
> `kind: 'script'` now returns an error pointing at those replacements.
| kind | directory | runtime | signature | use case |
|------|-----------|---------|-----------|----------|
| `'script'` (default) | `scripts/` | plain Node.js — no Chromium | `main({ params })` | Data processing, API calls, computation, file conversion |
| `'browser-macro'` | `browser-macros/` | Playwright — Chromium | `main({ context, params })` | Web automation with a live browser session |
| directory | runtime | signature | use case |
|-----------|---------|-----------|----------|
| `browser-macros/` | Playwright — Chromium | `main({ context, params })` | Web automation with a live browser session |
## Input
```ts
{
name: string, // filename — '.js' is appended if absent
params?: Record<string, unknown>, // runtime values matching the script's param spec
kind?: 'script' | 'browser-macro' // default: 'script'
params?: Record<string, unknown>, // runtime values matching the macro's param spec
}
```
## Param validation
Params are validated against the `params:` block in the script's YAML frontmatter:
Params are validated against the `params:` block in the macro's YAML frontmatter:
- Extra params not listed in the spec → error containing "param"
- Wrong type for a declared param → error containing "param"
- Missing required param (no default) → error containing "param"
@@ -29,9 +31,9 @@ Params are validated against the `params:` block in the script's YAML frontmatte
On any param error the tool returns `isError: true` immediately — no subprocess is spawned.
## Session integration (browser-macro only)
## Session integration
If a `browser-macro` script's frontmatter declares `session_profile_id: <N>`, the tool:
If the macro's frontmatter declares `session_profile_id: <N>`, the tool:
1. Loads the profile from the DB (owner-gated — must belong to `ctx.userId`).
2. Decrypts the user's envelope-encrypted DEK using the master key.
@@ -40,13 +42,12 @@ If a `browser-macro` script's frontmatter declares `session_profile_id: <N>`, th
If any step fails the tool returns `isError: true` with a descriptive message.
For `kind: 'script'` (plain runtime), `session_profile_id` in the frontmatter is ignored — no session is loaded.
## Self-healing recorder
## Self-healing recorder (browser-macro only)
When a `browser-macro` fails at runtime, the tool automatically enables the BrowseWeb recorder for the current task (if not already enabled). On task completion, `recording-flush` stages a candidate patch as `browser-macros/{name}.next.js` for diff review.
Plain scripts (`kind: 'script'`) do **not** auto-enable the recorder.
When a macro fails at runtime, the tool automatically enables the BrowseWeb
recorder for the current task (if not already enabled). On task completion,
`recording-flush` stages a candidate patch as `browser-macros/{name}.next.js`
for diff review.
## Output format
@@ -58,14 +59,9 @@ On success:
<console.log lines from the child process>
```
The result is JSON-stringified if it is an object or array; `String(result)` otherwise. The `[script logs]` section is only appended when the script produced logs.
The result is JSON-stringified if it is an object or array; `String(result)` otherwise. The `[script logs]` section is only appended when the macro produced logs.
On failure (plain):
```
RunUserScript "{name}" failed: <error message>
```
On failure (browser-macro):
On failure:
```
RunUserScript "{name}" failed: <error message>
@@ -78,36 +74,22 @@ On task complete, a candidate patch will be saved as browser-macros/{name}.next.
| Situation | `isError` | message contains |
|-----------|-----------|-----------------|
| No authenticated user | true | "authenticated" |
| Script file not found | true | "not found" |
| Macro file not found | true | "not found" |
| Retired `kind: 'script'` passed | true | "retired" |
| Frontmatter parse error | true | "frontmatter" |
| Param type / missing error | true | "param" |
| Session profile not found / not owned | true | "not found or does not belong" |
| Profile not active | true | "not active" |
| DEK / blob decryption failure | true | "decrypt" |
| Script timeout (60 s) | true | "timeout" |
| Script exits non-zero | true | "exited code" |
| Plain script denied child_process (e.g. spawning python) | true | "exited code" + "use the Bash tool" |
| Macro timeout (60 s) | true | "timeout" |
| Macro exits non-zero | true | "exited code" |
## Notes
- The tool is a META_TOOL — it is available in every movement without listing it in `allowed_tools`.
- Use `kind: 'browser-macro'` for any script that needs a browser (`context`).
- Use `ListUserAssets` first to discover available scripts and their param specs.
- On browser-macro failure, use `BrowseWeb` as a manual fallback.
## Running Python (don't — use Bash)
`RunUserScript` runs **Node only**. There is no Python interpreter path. A
common footgun is to write a Node script that does
`child_process.spawn('python3', ...)` and run it here — that **cannot work**:
plain scripts run under Node's `--permission` model, which denies
`child_process` entirely (you get `ERR_ACCESS_DENIED`). Even if it were
allowed, the child's env is scrubbed, so it would not see the orchestrator's
provisioned Python environment.
To run Python, use the **`Bash` tool** instead: `python3 your_script.py`. The
Bash sandbox has the pip packages pre-baked (pypdf, pdfplumber, python-docx,
python-pptx, openpyxl, pandas, numpy, …). That is the supported, working path.
- Use `ListUserAssets` first to discover available macros and their param specs.
- On macro failure, use `BrowseWeb` as a manual fallback.
- To run Python or other ad-hoc code, use the **Bash** tool (pip packages pre-baked).
## Security and trust model
@@ -118,13 +100,13 @@ tools:
user_scripts_enabled: true
```
**Only enable for trusted users.** User scripts run in a restricted child process:
- Env is scrubbed — only `PATH`, `HOME`, `TMPDIR/TMP`, `LANG`, `NODE_ENV`, and `PLAYWRIGHT_BROWSERS_PATH` are forwarded. API keys, database passwords, and other secrets in the orchestrator's environment are not visible to the script.
**Only enable for trusted users.** Macros run in a restricted child process:
- Env is scrubbed — only `PATH`, `HOME`, `TMPDIR/TMP`, `LANG`, `NODE_ENV`, and `PLAYWRIGHT_BROWSERS_PATH` are forwarded. API keys, database passwords, and other secrets in the orchestrator's environment are not visible to the macro.
- CWD is set to the system tmpdir, not the orchestrator workspace.
- Stdout is capped at 1 MB and stderr at 200 KB; exceeding either limit kills the child.
- On timeout, the entire process group (including Playwright's Chromium for browser-macros) is killed.
- On timeout, the entire process group (including Playwright's Chromium) is killed.
**The two runtimes have different capability levels:**
- **Plain scripts (`kind: 'script'`)** run under Node's Permissions Model (`--permission`): `--allow-fs-read` is limited to the child-runner dir and tmpdir, `--allow-fs-write` to tmpdir, and `child_process`, worker threads, and native addons are **denied**. A plain script that tries to spawn a subprocess (e.g. python) fails with `ERR_ACCESS_DENIED`. See "Running Python" above.
- **Browser-macros (`kind: 'browser-macro'`)** cannot use `--permission` — Chromium launch, native bindings, and outbound HTTPS all need unrestricted `child_process`/addons/network. They run with full Node.js capability (env-scrubbed only) and rely on container-level isolation. Treat them as trusted code.
Browser-macros cannot use Node's `--permission` model — Chromium launch, native
bindings, and outbound HTTPS all need unrestricted `child_process`/addons/network.
They run with full Node.js capability (env-scrubbed only) and rely on
container-level isolation. Treat them as trusted code.
+21 -44
View File
@@ -1,13 +1,16 @@
# WriteUserScript
Creates or overwrites a script in the caller's user folder.
Creates or overwrites a Playwright browser-macro in the caller's
`browser-macros/` folder.
Two destinations are supported:
> **Retired (2026-06):** plain-Node `scripts/` (the old `kind: 'script'`) and
> `templates/` were removed. Keep reusable procedures/boilerplate in **Skills**,
> and run ad-hoc code with the **Bash** tool. Passing `kind: 'script'` now
> returns an error pointing at those replacements.
| kind | directory | runtime | signature |
|------|-----------|---------|-----------|
| `'script'` (default) | `scripts/` | plain Node.js | `main({ params })` |
| `'browser-macro'` | `browser-macros/` | Playwright — Chromium | `main({ context, params })` |
| directory | runtime | signature |
|-----------|---------|-----------|
| `browser-macros/` | Playwright — Chromium | `main({ context, params })` |
## Input
@@ -15,7 +18,6 @@ Two destinations are supported:
{
name: string, // slug — '.js' appended if absent
content: string, // full file text (frontmatter + main())
kind?: 'script' | 'browser-macro', // default: 'script'
overwrite?: boolean // default: false — error if file exists
}
```
@@ -26,14 +28,14 @@ The content must define a `main` function. The following forms are all accepted:
```js
// ES function declaration
async function main({ params }) { }
async function main({ context, params }) { }
// Arrow / assigned function
const main = async ({ params }) => { };
const main = async ({ context, params }) => { };
// CommonJS export
module.exports = async function main({ params }) { };
exports.main = async function({ params }) { };
module.exports = async function main({ context, params }) { };
exports.main = async function({ context, params }) { };
```
If none of these patterns is found the tool returns `isError: true` with a
@@ -53,10 +55,10 @@ params:
---
```
Frontmatter is parsed by `RunUserScript` for param validation. Scripts without
Frontmatter is parsed by `RunUserScript` for param validation. Macros without
frontmatter still run, but param validation is skipped.
Browser macros may additionally declare `session_profile_id: <N>` to auto-load
Macros may additionally declare `session_profile_id: <N>` to auto-load
a saved login session (see `RunUserScript` docs).
## Size limit
@@ -70,41 +72,15 @@ Pass `overwrite: true` to replace the existing file atomically.
## When to use
- You discovered a useful reusable pattern during a task — save it for next time.
- The user asks you to create or update a script they can run later via `RunUserScript`.
- You discovered a useful browser-automation pattern during a task — save it for next time.
- The user asks you to create or update a macro they can run later via `RunUserScript`.
- You want to prototype a browser automation without going through the UI.
## Examples
### Plain Node script
```js
WriteUserScript({
name: "fetch-and-clean",
kind: "script",
content: `---
description: Fetch a URL and return cleaned JSON
params:
- name: url
type: string
---
const https = require('https');
async function main({ params }) {
const res = await fetch(params.url);
const json = await res.json();
return { items: json.items ?? [] };
}
`
})
```
### Browser macro
## Example
```js
WriteUserScript({
name: "screenshot-dashboard",
kind: "browser-macro",
content: `---
description: Take a screenshot of the dashboard
params:
@@ -126,6 +102,7 @@ async function main({ context, params }) {
| Situation | `isError` | message contains |
|-----------|-----------|-----------------|
| No authenticated user | true | "authenticated" |
| Retired `kind: 'script'` passed | true | "retired" |
| `name` missing / empty | true | `"name"` |
| `name` contains `/`, space, etc. | true | "alphanumeric" |
| `content` missing `main` | true | "main" |
@@ -135,5 +112,5 @@ async function main({ context, params }) {
## Notes
- `WriteUserScript` is a META_TOOL — available in every movement without listing it in `allowed_tools`.
- After writing, use `RunUserScript` to immediately execute and verify the script.
- Use `ListUserAssets` to see all scripts currently in the folder.
- After writing, use `RunUserScript` to immediately execute and verify the macro.
- Use `ListUserAssets` to see all macros currently in the folder.
-133
View File
@@ -1,133 +0,0 @@
# WriteUserTemplate
Creates or overwrites a Markdown template in the caller's `templates/` folder.
Templates written here are immediately usable by `ReadUserTemplate` (to inspect them) and `RenderUserTemplate` (to substitute `{{var}}` placeholders and apply defaults).
## Input
```ts
{
name: string, // slug — '.md' appended if absent
content: string, // full file text (optional frontmatter + Markdown body)
overwrite?: boolean // default: false — error if file exists
}
```
## File structure
The content is plain Markdown with an optional YAML frontmatter block:
```markdown
---
description: One-line description shown in ListUserAssets
params:
- name: date
type: string
- name: author
type: string
default: "Team"
---
# Report — {{date}}
Prepared by: {{author}}
## Highlights
...
```
Frontmatter is not required. Templates without `params:` render as-is (no substitution).
## Frontmatter params spec
Each param entry in `params:` supports:
| field | required | description |
|-------|----------|-------------|
| `name` | yes | placeholder name used as `{{name}}` in the body |
| `type` | yes | `string` \| `number` \| `boolean` |
| `default` | no | value applied when the param is omitted |
Required params (no default) must be supplied by the caller of `RenderUserTemplate`. Missing required params produce an error at render time.
## Size limit
128 KB (UTF-8 encoded). Exceeding the limit returns `isError: true`.
## Overwrite semantics
By default (`overwrite: false`) writing to an existing file is an error.
Pass `overwrite: true` to replace the file atomically.
## When to use
- You noticed a recurring structure (email skeleton, weekly report, issue template) — persist it for reuse.
- The user asks you to create or update a template so they can render it later.
- You want to encode a multi-step prompt skeleton with named slots.
## Examples
### Email template
```js
WriteUserTemplate({
name: "api-error-email",
content: `---
description: Customer-facing API error notification email
params:
- name: incident_id
type: string
- name: service
type: string
- name: eta
type: string
default: "unknown"
---
Dear Customer,
We have detected an issue with **{{service}}** (incident {{incident_id}}).
Our team is actively working on a resolution. Estimated resolution time: **{{eta}}**.
We apologize for the inconvenience.
— The Platform Team
`
})
```
### Report skeleton (no params)
```js
WriteUserTemplate({
name: "weekly-retro",
content: `# Weekly Retro
## What went well
-
## What to improve
-
## Action items
- [ ]
`
})
```
## Error cases
| Situation | `isError` | message contains |
|-----------|-----------|-----------------|
| No authenticated user | true | "authenticated" |
| `name` missing / empty | true | `"name"` |
| `name` contains `/`, space, etc. | true | "alphanumeric" |
| Content exceeds 128 KB | true | "bytes" |
| File exists, `overwrite` not set | true | "overwrite" |
## Notes
- `WriteUserTemplate` is a META_TOOL — available in every movement without listing it in `allowed_tools`.
- After writing, use `ReadUserTemplate` to verify the content and `RenderUserTemplate` to test substitution.
- Use `ListUserAssets` to see all templates currently in the folder.
+8 -37
View File
@@ -9,7 +9,7 @@ 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`.
- Storing reusable 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.
@@ -22,32 +22,21 @@ 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' })`.
> **Retired (2026-06):** the former `scripts/` (plain Node) and `templates/`
> subdirectories were removed. Reusable procedures/boilerplate belong in
> **Skills**; ad-hoc code runs via the agent's **Bash** tool. Existing files
> remain on disk but are no longer listed or runnable.
Generated automatically by the **Save as Script** button in the recordings panel. Can also be written manually in the UI. The agent runs them via `RunUserScript({ name })`.
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`
@@ -126,25 +115,7 @@ 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-Macro Format
### Browser macros (`browser-macros/`)