diff --git a/src/content/docs-es/automations.mdx b/src/content/docs-es/automations.mdx index d89b699..86966b2 100644 --- a/src/content/docs-es/automations.mdx +++ b/src/content/docs-es/automations.mdx @@ -23,7 +23,7 @@ El agente de una automatización no hace push ni publica nada por su cuenta. **P ## Antes de empezar -- **Un workspace encendido**, con Pi o Hermes instalado, como para cualquier [run](/es/docs/runs#antes-de-empezar). +- **Un workspace encendido**, con el agente que quieras instalado (Pi, Pi + Gentle Shell o Hermes), como para cualquier [run](/es/docs/runs#elige-el-agente). - **Un plan con inferencia.** Los runs de una automatización usan la clave de inferencia de tu workspace y cuentan para el uso de tu plan como cualquier otro run. - **Una sesión en el navegador.** Crear o cambiar automatizaciones, escribir secretos y aprobar acciones exige que hayas iniciado sesión en [cloud.nan.builders](https://cloud.nan.builders/) desde tu navegador, con el enlace por correo. Una sesión de la CLI, una API key o un token pueden listar automatizaciones y lanzarlas, pero nunca cambiarlas. Consulta [Por qué algunas acciones necesitan el navegador](#por-qué-algunas-acciones-necesitan-el-navegador). - **Para las plantillas de GitHub, un token de GitHub guardado como secreto.** Sin él la automatización no se puede guardar, así que créalo antes: consulta [Un token de GitHub para las acciones](#un-token-de-github-para-las-acciones). @@ -58,7 +58,7 @@ Las tres plantillas de GitHub actúan sobre un repositorio, así que necesitan u | **Description (optional)** | Una descripción, para ti. | | **Instructions** | Lo que tiene que hacer el agente, hasta 16 KiB. Tus instrucciones son de confianza. Lo que envíe un webhook o un run manual le llega al agente como datos, nunca como instrucciones. | | **Workspace** | El workspace donde trabaja el agente. | -| **Agent** | Pi o Hermes. | +| **Agent** | **Pi**, **Pi + Gentle Shell** o **Hermes**. Solo los agentes instalados en el **Workspace** elegido, así que elige antes el workspace. Un agente que quites luego del workspace aparece como **(not installed)** y sus runs se rechazan con `agent_not_installed`. Consulta [Elige el agente](/es/docs/runs#elige-el-agente). | | **Folder** | Dónde trabaja el agente, dentro de `/home/nan`. No se usa con **Fresh clone per run**. | | **Separate branch** | Rama aparte: **On** (por defecto), **Auto** u **Off (work in the folder directly)** (trabajar directamente en la carpeta), como en un [run](/es/docs/runs#dónde-trabaja-el-agente). No se usa con **Fresh clone per run**. | | **Time limit (minutes)** | El tiempo máximo, de 1 a 120. 30 por defecto. | diff --git a/src/content/docs-es/pr-gatekeeper.mdx b/src/content/docs-es/pr-gatekeeper.mdx index 3b9f3e1..afc4086 100644 --- a/src/content/docs-es/pr-gatekeeper.mdx +++ b/src/content/docs-es/pr-gatekeeper.mdx @@ -90,7 +90,7 @@ Y cómo se comporta: ## Antes de empezar - **Un workspace encendido con las herramientas de tu repositorio.** El agente ejecuta los tests en uno de tus [workspaces](/es/docs/workspaces), así que necesita lo mismo que tu CI: Go, Node, Python... Instálalas una vez en el workspace, como lo harías en tu portátil, y mantenlo encendido: un workspace parado no puede lanzar runs, y los eventos que llegan mientras tanto se pierden. El ejemplo usa un workspace llamado `work` con Go instalado. -- **Pi o Hermes instalado en ese workspace**, desde su [panel de Software](/es/docs/workspaces#agentes-y-el-panel-de-software). El ejemplo usa Pi. +- **El agente instalado en ese workspace**: Pi, Pi + Gentle Shell o Hermes, desde su [panel de Software](/es/docs/workspaces#agentes-y-el-panel-de-software). La automatización solo puede usar un agente que el workspace tenga (consulta [Elige el agente](/es/docs/runs#elige-el-agente)). El ejemplo usa Pi. - **Un plan con inferencia.** Cada review es un [run de agente](/es/docs/runs) y usa tu plan como cualquier otra petición. - **Acceso de administrador al repositorio en GitHub**, para añadir el webhook y crear los tokens. - **Una sesión en el navegador en [cloud.nan.builders](https://cloud.nan.builders/)**, con el enlace del correo: la necesitas para crear automatizaciones y guardar secretos. diff --git a/src/content/docs-es/runs.mdx b/src/content/docs-es/runs.mdx index 88cfa42..8a58835 100644 --- a/src/content/docs-es/runs.mdx +++ b/src/content/docs-es/runs.mdx @@ -12,7 +12,7 @@ import Screenshot from '../../components/docs/Screenshot.astro'; # Runs de agentes. -Un **run** le da una tarea a un agente dentro de uno de tus [workspaces](/es/docs/workspaces) y lo deja trabajar solo. Escribes lo que quieres que haga, eliges el agente (Pi o Hermes, el que tengas instalado en ese workspace) y la carpeta, y el run se encarga del resto. Sigue adelante aunque cierres la página o la terminal. +Un **run** le da una tarea a un agente dentro de uno de tus [workspaces](/es/docs/workspaces) y lo deja trabajar solo. Escribes lo que quieres que haga, eliges el agente (Pi, Pi + Gentle Shell o Hermes, entre los que tengas instalados en ese workspace) y la carpeta, y el run se encarga del resto. Sigue adelante aunque cierres la página o la terminal. Mientras trabaja, sigues su log en directo. Cuando termina tienes su resultado y, si la carpeta es un repositorio git, una rama con sus cambios lista para que la revises. @@ -26,9 +26,27 @@ Para lanzar la misma tarea una y otra vez, desde un webhook, con una programaci ## Antes de empezar -- **Un workspace encendido**, con Pi o Hermes instalado. Puedes instalar cualquiera de los dos desde el panel de **Software** del workspace. +- **Un workspace encendido**, con al menos un agente instalado: Pi, Pi + Gentle Shell o Hermes. Se instalan desde el [panel de Software](/es/docs/workspaces#agentes-y-el-panel-de-software) del workspace. Consulta [Elige el agente](#elige-el-agente). - **Un plan con inferencia.** Un run usa la clave de inferencia del propio workspace, y lo que gasta el agente cuenta para el uso de tu plan como cualquier otra petición. Si tu plan no incluye inferencia no puedes crear runs: el portal te enseña las opciones para mejorarlo. +## Elige el agente + +Un run puede usar uno de tres agentes. Cada uno necesita sus programas instalados en el workspace: + +| Agente | Valor de `--agent` y `agent` | Qué necesita el workspace | +|---|---|---| +| **Pi** | `pi` | Pi. | +| **Pi + Gentle Shell** | `pi_gentle` | Pi y gentle-shell. El run arranca Pi a través de gentle-shell, así que el agente trabaja con los subagentes, los guardarraíles y la memoria de gentle-shell. | +| **Hermes** | `hermes` | Hermes, elegido al crear el workspace o instalado desde el panel de **Software**. | + +Los runs y las automatizaciones solo aceptan un agente que el workspace tenga instalado: + +- **En el portal**, la lista **Agent** de un run nuevo o de una automatización solo muestra los agentes de ese workspace. Si no tiene ninguno, el portal te lo indica y enlaza a su panel de **Software**: instala uno ahí primero. +- **Desde la CLI o la API**, pedir un agente que el workspace no tiene se rechaza antes de crear el run, con `409 agent_not_installed` (el error señala el campo `agent`). `nan run` sale con código 4. Instala el agente desde el panel de **Software**, o elige otro, y vuelve a intentarlo. +- **Una automatización** conserva su agente aunque luego lo quites del workspace (el editor lo marca **(not installed)**), pero sus runs se rechazan hasta que lo vuelvas a instalar o elijas otro agente. Si cambias el workspace de una automatización y el nuevo no tiene su agente, el editor pasa a uno que sí tenga y te lo indica. + +Para saber desde un script qué agentes puede usar un workspace, lee su campo `run_agents`: consulta [Referencia de la API → Workspaces](/es/docs/api#tag/workspaces). + ## Lanza un run desde el portal @@ -45,7 +63,7 @@ Pulsa **New run** y rellena el formulario (el portal está en inglés): | Campo | Qué es | |---|---| -| **Agent** | El agente: Pi o Hermes, el que tengas instalado en el workspace. | +| **Agent** | El agente: **Pi**, **Pi + Gentle Shell** o **Hermes**. La lista solo muestra los agentes instalados en el workspace. Consulta [Elige el agente](#elige-el-agente). | | **Task** | La tarea, con tus palabras, como se la escribirías al agente en un chat. Hasta 32 KiB (el contador bajo la caja te dice los bytes que llevas). | | **Folder** | La carpeta donde trabaja el agente. Por defecto es `/home/nan` y tiene que estar dentro de ella. | | **Separate branch** | Rama aparte: **Auto** (por defecto), **On** u **Off**. En un repositorio git, el agente trabaja en su propia rama y tu checkout se queda como está. Consulta [Dónde trabaja el agente](#dónde-trabaja-el-agente). | @@ -136,7 +154,7 @@ Los secretos que guardas en tus ajustes no se les dan a estos runs: solo llegan ## Desde la terminal: `nan run` -La [CLI de NaN](/es/docs/nan-cli) lanza y sigue runs desde tu propia terminal. Necesitas la versión **0.1.25** o posterior: `nan --version` imprime `nan `. +La [CLI de NaN](/es/docs/nan-cli) lanza y sigue runs desde tu propia terminal. Necesitas la versión **0.1.25** o posterior, y la **0.1.29** o posterior para `--agent pi_gentle`: `nan --version` imprime `nan `. @@ -170,6 +188,9 @@ Otras formas de lanzarlo: # Un workspace, un agente y una carpeta concretos nan run --ws develop --agent hermes --cwd /home/nan/projects/api "revisa los TODO pendientes" +# Pi + Gentle Shell (el workspace necesita Pi y gentle-shell) +nan run --ws develop --agent pi_gentle "refactoriza el cargador de configuración sin romper los tests" + # La tarea desde un archivo, o desde stdin nan run -f tarea.md git diff | nan run - @@ -181,7 +202,7 @@ nan run --detach "actualiza las dependencias" | Flag | Por defecto | Qué hace | |---|---|---| | `--ws NAME` | tu único workspace | El workspace donde se ejecuta. Si tienes más de uno y no lo pones, la CLI te los lista y sale con código 64. | -| `--agent pi\|hermes` | `pi` | El agente. | +| `--agent pi\|pi_gentle\|hermes` | `pi` | El agente: `pi` (Pi), `pi_gentle` (Pi + Gentle Shell) o `hermes` (Hermes). Tiene que estar instalado en el workspace. Consulta [Elige el agente](#elige-el-agente). | | `--model M` | el del agente | El modelo que usa el agente. | | `--cwd PATH` | `/home/nan` | La carpeta en el workspace. | | `--worktree` / `--no-worktree` | auto | Fuerza la rama aparte a on u off. Consulta [Dónde trabaja el agente](#dónde-trabaja-el-agente). | @@ -345,6 +366,19 @@ fi Los campos equivalen al formulario del portal: **Agent** es `agent`, **Task** es `prompt`, **Folder** es `cwd`, y los opcionales **Separate branch** y **Time limit** son `git_isolation` y `timeout_seconds`. +`agent` es `pi`, `pi_gentle` o `hermes`, como en [Elige el agente](#elige-el-agente). Si el workspace no tiene ese agente instalado, no se crea ningún run y el ejemplo imprime el error: + +```json +{ + "error": { + "message": "Pi + Gentle Shell is not installed in this workspace", + "type": "invalid_request_error", + "param": "agent", + "code": "agent_not_installed" + } +} +``` + El stream manda un frame `run_event` por cada evento del log, un frame `state` cuando el run cambia de estado y un frame `end` con el run terminado, y después se cierra. Referencia completa: [Referencia de la API → Runs](/es/docs/api#tag/runs). Ahí están todos los endpoints (listar, consultar, cancelar), los campos de cada petición y respuesta, los tipos de evento, cómo retomar un stream, los reintentos idempotentes y todos los códigos de error. Para sacar el nombre o el id de un workspace desde un script, consulta [Referencia de la API → Workspaces](/es/docs/api#tag/workspaces). diff --git a/src/content/docs-es/workspaces.mdx b/src/content/docs-es/workspaces.mdx index 3a9494e..f417822 100644 --- a/src/content/docs-es/workspaces.mdx +++ b/src/content/docs-es/workspaces.mdx @@ -211,13 +211,13 @@ El panel **Software**, en la pestaña Overview del workspace, lista los agentes - **Actualizar** uno, o todos a la vez, cuando sale una versión nueva. Las actualizaciones conservan tus archivos, sesiones y ajustes. - **Quitar** uno que ya no uses. -gentle-shell es un complemento de Pi (memoria entre sesiones con Engram), así que necesita Pi instalado. Si tu Pi es demasiado antiguo, el panel te ofrece actualizarlo primero. +gentle-shell es un complemento de Pi (memoria entre sesiones con Engram), así que necesita Pi instalado. Si tu Pi es demasiado antiguo, el panel te ofrece actualizarlo primero. Con los dos instalados, los runs y las automatizaciones también pueden usar **Pi + Gentle Shell**: consulta [Elige el agente](/es/docs/runs#elige-el-agente). Con un plan de inferencia, los agentes vienen configurados para usar los modelos de NaN con tu membresía. Las guías de [Pi](/es/docs/pi) y [Hermes](/es/docs/hermes) explican cómo cambiar el modelo o el proveedor a mano. ## Pon a tus agentes a trabajar en tareas -No hace falta que te quedes conectado mientras un agente trabaja. Un **run** le da una tarea a Pi o a Hermes dentro de tu workspace y lo deja trabajar solo: sigues su log en directo, continúa aunque cierres la página y, en un repositorio git, sus cambios acaban en una rama para que los revises. Lánzalo desde la pestaña **Runs** del workspace, desde tu terminal con `nan run` o desde la API. +No hace falta que te quedes conectado mientras un agente trabaja. Un **run** le da una tarea a un agente instalado en tu workspace (Pi, Pi + Gentle Shell o Hermes) y lo deja trabajar solo: sigues su log en directo, continúa aunque cierres la página y, en un repositorio git, sus cambios acaban en una rama para que los revises. Lánzalo desde la pestaña **Runs** del workspace, desde tu terminal con `nan run` o desde la API. Cómo funciona, los límites y los comandos están en [Runs de agentes](/es/docs/runs). diff --git a/src/content/docs/automations.mdx b/src/content/docs/automations.mdx index bb4dbfa..3f9671e 100644 --- a/src/content/docs/automations.mdx +++ b/src/content/docs/automations.mdx @@ -23,7 +23,7 @@ The agent of an automation does not push or post on its own. It **proposes actio ## Before you start -- **A workspace that is running**, with Pi or Hermes installed, as for any [run](/docs/runs#before-you-start). +- **A workspace that is running**, with the agent you want installed (Pi, Pi + Gentle Shell or Hermes), as for any [run](/docs/runs#pick-the-agent). - **A plan with inference.** Automation runs use your workspace's inference key and count against your plan's usage like any other run. - **A browser sign-in.** Creating or changing automations, writing secrets and approving actions need you signed in at [cloud.nan.builders](https://cloud.nan.builders/) in your browser, with the email link. A CLI session, an API key or a token can list automations and run them, but never change them. See [Why some actions need the browser](#why-some-actions-need-the-browser). - **For the GitHub templates, a GitHub token saved as a secret.** The automation cannot be saved without it, so create it first: see [A GitHub token for actions](#a-github-token-for-actions). @@ -58,7 +58,7 @@ The three GitHub templates act on a repository, so they need a **GitHub reposito | **Description (optional)** | For you. | | **Instructions** | What the agent must do, up to 16 KiB. Your instructions are trusted. Whatever a webhook or a manual run sends is given to the agent as data, never as instructions. | | **Workspace** | The workspace the agent runs in. | -| **Agent** | Pi or Hermes. | +| **Agent** | **Pi**, **Pi + Gentle Shell** or **Hermes**. Only the agents installed in the chosen **Workspace**, so pick the workspace first. An agent removed from the workspace later shows as **(not installed)** and its runs are refused with `agent_not_installed`. See [Pick the agent](/docs/runs#pick-the-agent). | | **Folder** | Where the agent works, inside `/home/nan`. Not used with **Fresh clone per run**. | | **Separate branch** | **On** (the default), **Auto** or **Off (work in the folder directly)**, as in a [run](/docs/runs#where-the-agent-works). Not used with **Fresh clone per run**. | | **Time limit (minutes)** | From 1 to 120. 30 by default. | diff --git a/src/content/docs/pr-gatekeeper.mdx b/src/content/docs/pr-gatekeeper.mdx index da4de26..92ff9e1 100644 --- a/src/content/docs/pr-gatekeeper.mdx +++ b/src/content/docs/pr-gatekeeper.mdx @@ -90,7 +90,7 @@ And how it behaves: ## Before you start - **A running workspace with your repository's toolchain.** The agent runs the tests in one of your [workspaces](/docs/workspaces), so it needs what your CI needs: Go, Node, Python... Install them once in the workspace, as you would on your laptop, and keep it running: a stopped workspace cannot start runs, and the events that arrive meanwhile are lost. The example uses a workspace called `work` with Go installed. -- **Pi or Hermes installed in that workspace**, from its [Software panel](/docs/workspaces#agents-and-the-software-panel). The example uses Pi. +- **The agent installed in that workspace**: Pi, Pi + Gentle Shell or Hermes, from its [Software panel](/docs/workspaces#agents-and-the-software-panel). The automation can only use an agent the workspace has (see [Pick the agent](/docs/runs#pick-the-agent)). The example uses Pi. - **A plan with inference.** Each review is an [agent run](/docs/runs) and uses your plan like any other request. - **Admin access to the repository on GitHub**, to add the webhook and create the tokens. - **A browser sign-in at [cloud.nan.builders](https://cloud.nan.builders/)**, with the email link: creating automations and saving secrets need it. diff --git a/src/content/docs/runs.mdx b/src/content/docs/runs.mdx index 0a55582..68ee602 100644 --- a/src/content/docs/runs.mdx +++ b/src/content/docs/runs.mdx @@ -12,7 +12,7 @@ import Screenshot from '../../components/docs/Screenshot.astro'; # Agent runs. -A **run** gives an agent a task inside one of your [workspaces](/docs/workspaces) and lets it work on its own. You write what you want done, pick the agent (Pi or Hermes, whichever is installed in that workspace) and the folder, and the run takes it from there. It keeps going if you close the page or the terminal. +A **run** gives an agent a task inside one of your [workspaces](/docs/workspaces) and lets it work on its own. You write what you want done, pick the agent (Pi, Pi + Gentle Shell or Hermes, among the ones installed in that workspace) and the folder, and the run takes it from there. It keeps going if you close the page or the terminal. While it works you follow its log live. When it ends you get its result and, if the folder is a git repository, a branch with its changes, ready for you to review. @@ -26,9 +26,27 @@ To run the same task again and again, from a webhook, on a schedule or with acti ## Before you start -- **A workspace that is running**, with Pi or Hermes installed. You can install either one from the **Software** panel of the workspace. +- **A workspace that is running**, with at least one agent installed: Pi, Pi + Gentle Shell or Hermes. You install them from the [Software panel](/docs/workspaces#agents-and-the-software-panel) of the workspace. See [Pick the agent](#pick-the-agent). - **A plan with inference.** A run uses your workspace's own inference key, and what the agent spends counts against your plan's usage like any other request. Without inference in your plan you cannot create runs: the portal shows you the upgrade options instead. +## Pick the agent + +A run can use one of three agents. Each one needs its programs installed in the workspace: + +| Agent | Value for `--agent` and `agent` | What the workspace needs | +|---|---|---| +| **Pi** | `pi` | Pi. | +| **Pi + Gentle Shell** | `pi_gentle` | Pi and gentle-shell. The run starts Pi through gentle-shell, so the agent works with gentle-shell's subagents, guardrails and memory. | +| **Hermes** | `hermes` | Hermes, picked when you created the workspace or installed from the **Software** panel. | + +Runs and automations only accept an agent the workspace has installed: + +- **In the portal**, the **Agent** list of a new run or an automation shows only the agents of that workspace. If it has none, the portal says so and links to its **Software** panel: install one there first. +- **From the CLI or the API**, asking for an agent the workspace does not have is refused before the run is created, with `409 agent_not_installed` (the error names the `agent` field). `nan run` exits with code 4. Install the agent from the **Software** panel, or pick another one, and try again. +- **An automation** keeps its agent if you remove that agent from the workspace later (the editor marks it **(not installed)**), but its runs are refused until you install it again or pick another agent. If you change an automation's workspace and the new one lacks its agent, the editor switches to one it has and tells you. + +To know from a script which agents a workspace can run, read its `run_agents` field: see [API reference → Workspaces](/docs/api#tag/workspaces). + ## Start a run from the portal @@ -45,7 +63,7 @@ Press **New run** and fill in the form: | Field | What it is | |---|---| -| **Agent** | Pi or Hermes, whichever is installed in the workspace. | +| **Agent** | **Pi**, **Pi + Gentle Shell** or **Hermes**. The list shows only the agents installed in the workspace. See [Pick the agent](#pick-the-agent). | | **Task** | What you want done, in your own words, as you would write it to the agent in a chat. Up to 32 KiB (the counter under the box shows the bytes used). | | **Folder** | Where the agent works. It defaults to `/home/nan` and must be inside it. | | **Separate branch** | **Auto** (the default), **On** or **Off**. In a git repository, the agent works on its own branch and your checkout stays as it is. See [Where the agent works](#where-the-agent-works). | @@ -136,7 +154,7 @@ The secrets you keep in your settings are not given to these runs: they go only ## From the terminal: `nan run` -The [NaN CLI](/docs/nan-cli) starts and follows runs from your own terminal. You need version **0.1.25** or newer: `nan --version` prints `nan `. +The [NaN CLI](/docs/nan-cli) starts and follows runs from your own terminal. You need version **0.1.25** or newer, and **0.1.29** or newer for `--agent pi_gentle`: `nan --version` prints `nan `. @@ -170,6 +188,9 @@ A few more ways to start one: # A specific workspace, agent and folder nan run --ws develop --agent hermes --cwd /home/nan/projects/api "review the open TODOs" +# Pi + Gentle Shell (the workspace needs Pi and gentle-shell) +nan run --ws develop --agent pi_gentle "refactor the config loader and keep the tests green" + # The task from a file, or from stdin nan run -f task.md git diff | nan run - @@ -181,7 +202,7 @@ nan run --detach "upgrade the dependencies" | Flag | Default | What it does | |---|---|---| | `--ws NAME` | your only workspace | The workspace to run in. If you have more than one and leave it out, the CLI lists them and exits with code 64. | -| `--agent pi\|hermes` | `pi` | The agent. | +| `--agent pi\|pi_gentle\|hermes` | `pi` | The agent: `pi` (Pi), `pi_gentle` (Pi + Gentle Shell) or `hermes` (Hermes). It must be installed in the workspace. See [Pick the agent](#pick-the-agent). | | `--model M` | the agent's own | The model the agent uses. | | `--cwd PATH` | `/home/nan` | The folder in the workspace. | | `--worktree` / `--no-worktree` | auto | Force a separate branch on or off. See [Where the agent works](#where-the-agent-works). | @@ -345,6 +366,19 @@ fi The fields match the portal form: **Agent** is `agent`, **Task** is `prompt`, **Folder** is `cwd`, and the optional **Separate branch** and **Time limit** are `git_isolation` and `timeout_seconds`. +`agent` is `pi`, `pi_gentle` or `hermes`, as in [Pick the agent](#pick-the-agent). If the workspace does not have that agent installed, no run is created and the example prints the error instead: + +```json +{ + "error": { + "message": "Pi + Gentle Shell is not installed in this workspace", + "type": "invalid_request_error", + "param": "agent", + "code": "agent_not_installed" + } +} +``` + The stream sends one `run_event` frame per event of the log, a `state` frame when the run changes state, and an `end` frame with the finished run, after which it closes. Full reference: [API reference → Runs](/docs/api#tag/runs). It has every endpoint (list, retrieve, cancel), the request and response fields, the event types, how to resume a stream, idempotent retries and every error code. To find a workspace's name or id from a script, see [API reference → Workspaces](/docs/api#tag/workspaces). diff --git a/src/content/docs/workspaces.mdx b/src/content/docs/workspaces.mdx index 1f74a54..c29cbeb 100644 --- a/src/content/docs/workspaces.mdx +++ b/src/content/docs/workspaces.mdx @@ -211,13 +211,13 @@ The **Software** panel on the workspace's Overview tab lists the agents and tool - **Update** one, or everything at once, when a new version is out. Updates keep your files, sessions and settings. - **Remove** one you no longer use. -gentle-shell is an add-on for Pi (memory between sessions with Engram), so it needs Pi installed. If your Pi is too old for it, the panel offers to update Pi first. +gentle-shell is an add-on for Pi (memory between sessions with Engram), so it needs Pi installed. If your Pi is too old for it, the panel offers to update Pi first. With both installed, runs and automations can also use **Pi + Gentle Shell**: see [Pick the agent](/docs/runs#pick-the-agent). On an inference plan, the agents come set up to use NaN models with your membership. The setup guides for [Pi](/docs/pi) and [Hermes](/docs/hermes) explain how to change the model or the provider by hand. ## Run agents on tasks -You do not have to stay connected while an agent works. A **run** gives Pi or Hermes a task inside your workspace and lets it work on its own: you follow its log live, it keeps going when you close the page, and in a git repository its changes land on a branch for you to review. Start one from the **Runs** tab of the workspace, from your terminal with `nan run`, or from the API. +You do not have to stay connected while an agent works. A **run** gives an agent installed in your workspace (Pi, Pi + Gentle Shell or Hermes) a task and lets it work on its own: you follow its log live, it keeps going when you close the page, and in a git repository its changes land on a branch for you to review. Start one from the **Runs** tab of the workspace, from your terminal with `nan run`, or from the API. How it works, the limits and the commands are in [Agent runs](/docs/runs). diff --git a/src/data/openapi.json b/src/data/openapi.json index 0dce818..9a188cd 100644 --- a/src/data/openapi.json +++ b/src/data/openapi.json @@ -60,7 +60,7 @@ }, { "name": "Runs", - "description": "Give an agent (Pi or Hermes) a task inside one of your workspaces and follow it. A run keeps going if the client disconnects. Accepts a member API key or a platform token with the `runs` scope. Guide: [Agent runs](https://nan.builders/docs/runs)." + "description": "Give an agent (Pi, Pi + Gentle Shell or Hermes) a task inside one of your workspaces and follow it. A run keeps going if the client disconnects. Accepts a member API key or a platform token with the `runs` scope. Guide: [Agent runs](https://nan.builders/docs/runs)." }, { "name": "Automations", @@ -1794,18 +1794,34 @@ } }, "409": { - "description": "`workspace_not_running`: start the workspace first. `agent_not_installed`: install the agent from the workspace's Software panel. `no_inference_key`: the workspace has no inference key. `idempotency_conflict`: the `Idempotency-Key` was used with a different body.", + "description": "`workspace_not_running`: start the workspace first. `agent_not_installed` (`param: \"agent\"`): the workspace does not have that agent installed (see its `run_agents`); install it from the workspace's Software panel or pick another. `no_inference_key`: the workspace has no inference key. `idempotency_conflict`: the `Idempotency-Key` was used with a different body.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" }, - "example": { - "error": { - "message": "the workspace is not running", - "type": "invalid_request_error", - "param": null, - "code": "workspace_not_running" + "examples": { + "workspace_not_running": { + "summary": "Stopped workspace.", + "value": { + "error": { + "message": "the workspace is not running", + "type": "invalid_request_error", + "param": null, + "code": "workspace_not_running" + } + } + }, + "agent_not_installed": { + "summary": "The agent is not installed in the workspace.", + "value": { + "error": { + "message": "Pi + Gentle Shell is not installed in this workspace", + "type": "invalid_request_error", + "param": "agent", + "code": "agent_not_installed" + } + } } } } @@ -3500,7 +3516,7 @@ } }, "409": { - "description": "The run cannot start: the automation is disabled (`automation_disabled`), its workspace is not running (`workspace_not_running`), the agent is not installed (`agent_not_installed`), the workspace has no inference key (`no_inference_key`), or the `Idempotency-Key` was used with a different input (`idempotency_conflict`).", + "description": "The run cannot start: the automation is disabled (`automation_disabled`), its workspace is not running (`workspace_not_running`), the automation's agent is not installed in its workspace (`agent_not_installed`, `param: \"agent\"`), the workspace has no inference key (`no_inference_key`), or the `Idempotency-Key` was used with a different input (`idempotency_conflict`).", "content": { "application/json": { "schema": { @@ -3528,6 +3544,17 @@ "code": "workspace_not_running" } } + }, + "agent_not_installed": { + "summary": "The automation's agent is not installed in its workspace.", + "value": { + "error": { + "message": "Hermes is not installed in this workspace", + "type": "invalid_request_error", + "param": "agent", + "code": "agent_not_installed" + } + } } } } @@ -4136,6 +4163,11 @@ "pi", "hermes" ], + "run_agents": [ + "pi", + "pi_gentle", + "hermes" + ], "status": "running", "ssh_command": "ssh 3b0e8f52-1c7a-4d29-9e61-0a4f2b7c8d15@ssh.nan.builders -p 30222", "vcpus": 2, @@ -4257,6 +4289,11 @@ "pi", "hermes" ], + "run_agents": [ + "pi", + "pi_gentle", + "hermes" + ], "status": "running", "ssh_command": "ssh 3b0e8f52-1c7a-4d29-9e61-0a4f2b7c8d15@ssh.nan.builders -p 30222", "vcpus": 2, @@ -5350,9 +5387,10 @@ "type": "string", "enum": [ "pi", + "pi_gentle", "hermes" ], - "description": "The agent. It must be installed in the workspace, otherwise `409 agent_not_installed`." + "description": "The agent: `pi` (Pi), `pi_gentle` (Pi + Gentle Shell: Pi started through gentle-shell) or `hermes` (Hermes). It must be installed in the workspace (see `run_agents` on the workspace), otherwise `409 agent_not_installed` with `param: \"agent\"`." }, "prompt": { "type": "string", @@ -5524,8 +5562,10 @@ "type": "string", "enum": [ "pi", + "pi_gentle", "hermes" - ] + ], + "description": "The agent: `pi` (Pi), `pi_gentle` (Pi + Gentle Shell: Pi started through gentle-shell) or `hermes` (Hermes)." }, "model": { "type": [ @@ -5879,6 +5919,7 @@ "type": "string", "enum": [ "pi", + "pi_gentle", "hermes" ] }, @@ -6270,6 +6311,18 @@ }, "description": "Agents and tools chosen for the workspace." }, + "run_agents": { + "type": "array", + "items": { + "type": "string", + "enum": [ + "pi", + "pi_gentle", + "hermes" + ] + }, + "description": "The agents a run or an automation can use in this workspace, in display order (`pi`, `pi_gentle`, `hermes`). `pi` needs Pi installed, `pi_gentle` needs Pi and gentle-shell, `hermes` needs Hermes chosen for the workspace or installed. A run (from `POST /v1/runs` or from an automation) with an agent that is not listed here is refused with `409 agent_not_installed`. Empty when no agent is installed." + }, "status": { "type": "string", "enum": [ @@ -6797,8 +6850,10 @@ "type": "string", "enum": [ "pi", + "pi_gentle", "hermes" - ] + ], + "description": "The agent: `pi` (Pi), `pi_gentle` (Pi + Gentle Shell: Pi started through gentle-shell) or `hermes` (Hermes). Creating an automation, moving it to another workspace, changing its agent or switching it back on needs the agent installed in the workspace (`409 agent_not_installed`). If the agent is removed from the workspace later, the automation keeps it and its runs are refused with `agent_not_installed`." }, "model": { "type": [ diff --git a/src/lib/openapiRuns.test.ts b/src/lib/openapiRuns.test.ts index 0f47f8f..7a2dcd5 100644 --- a/src/lib/openapiRuns.test.ts +++ b/src/lib/openapiRuns.test.ts @@ -97,7 +97,7 @@ describe('openapi.json: the Runs contract (cloud-api)', () => { expect(Object.keys(body.properties)).toEqual([ 'workspace', 'agent', 'prompt', 'cwd', 'git_isolation', 'timeout_seconds', 'model', 'trigger', ]); - expect(body.properties.agent.enum).toEqual(['pi', 'hermes']); + expect(body.properties.agent.enum).toEqual(['pi', 'pi_gentle', 'hermes']); expect(body.properties.prompt.description).toContain('32768 bytes'); expect(body.properties.cwd.default).toBe('/home/nan'); expect(body.properties.git_isolation.type).toEqual(['boolean', 'null']); @@ -351,3 +351,95 @@ describe('the text the docs API serves for the reference', () => { expect(added).not.toContain('—'); }); }); + +/** + * The run agents (cloud-api #345): `pi`, `pi_gentle` (Pi + Gentle Shell) and + * `hermes`, in the display order of software.RunAgentIDs. Every agent enum of + * the reference mirrors protocol.Agents; the workspace object carries + * `run_agents`; an agent the workspace lacks is `409 agent_not_installed` + * with `param: "agent"` and a message naming the agent (runs.AgentName). + */ +describe('openapi.json: run agents (Pi, Pi + Gentle Shell, Hermes)', () => { + const AGENTS = ['pi', 'pi_gentle', 'hermes']; + const sc = s.components.schemas; + + it.each([ + ['CreateRunRequest', sc.CreateRunRequest.properties.agent], + ['Run', sc.Run.properties.agent], + ['RunEventDataStarted', sc.RunEventDataStarted.properties.agent], + ['Automation', sc.Automation.properties.agent], + ])('%s.agent is exactly pi, pi_gentle, hermes', (_name, prop) => { + expect(prop.enum).toEqual(AGENTS); + }); + + it('no schema anywhere still lists only pi and hermes', () => { + const stale: string[] = []; + const walk = (node: any, path: string) => { + if (Array.isArray(node)) return node.forEach((v, i) => walk(v, `${path}/${i}`)); + if (!node || typeof node !== 'object') return; + if (Array.isArray(node.enum) && node.enum.includes('hermes') && !node.enum.includes('pi_gentle')) stale.push(path); + for (const [k, v] of Object.entries(node)) walk(v, `${path}/${k}`); + }; + walk(s, ''); + expect(stale).toEqual([]); + }); + + it('describes each agent value by its portal label', () => { + for (const prop of [sc.CreateRunRequest.properties.agent, sc.Run.properties.agent, sc.Automation.properties.agent]) { + expect(prop.description).toContain('`pi` (Pi)'); + expect(prop.description).toContain('`pi_gentle` (Pi + Gentle Shell'); + expect(prop.description).toContain('`hermes` (Hermes)'); + } + expect(sc.CreateRunRequest.properties.agent.description).toMatch(/`409 agent_not_installed` with `param: "agent"`/); + expect(s.tags.find((t: any) => t.name === 'Runs').description).toContain('(Pi, Pi + Gentle Shell or Hermes)'); + }); + + it('the workspace carries run_agents, right after agent_selection, with the same enum', () => { + const keys = Object.keys(sc.Workspace.properties); + expect(keys.indexOf('run_agents')).toBe(keys.indexOf('agent_selection') + 1); + const ra = sc.Workspace.properties.run_agents; + expect(ra.type).toBe('array'); + expect(ra.items.enum).toEqual(AGENTS); + expect(ra.description).toMatch(/display order/); + expect(ra.description).toContain('`pi_gentle` needs Pi and gentle-shell'); + expect(ra.description).toContain('409 agent_not_installed'); + }); + + it('both workspace examples show run_agents in display order', () => { + const list = op('/api/workspaces', 'get').responses['200'].content['application/json'].example; + const one = op('/api/workspaces/{id}', 'get').responses['200'].content['application/json'].example; + for (const ws of [list[0], one]) { + expect(ws.run_agents).toEqual(AGENTS.filter((a) => ws.run_agents.includes(a))); + expect(ws.run_agents).toContain('pi_gentle'); + } + }); + + it.each([ + ['/runs', 'Pi + Gentle Shell'], + ['/automations/{id}/runs', 'Hermes'], + ])('POST %s: the 409 agent_not_installed example names the agent and the agent field', (path, label) => { + const r = op(path, 'post').responses['409']; + const ex = r.content['application/json'].examples.agent_not_installed.value; + expect(ex).toEqual({ + error: { message: `${label} is not installed in this workspace`, type: 'invalid_request_error', param: 'agent', code: 'agent_not_installed' }, + }); + expect(validate(ex, r.content['application/json'].schema)).toEqual([]); + expect(r.description).toContain('`agent_not_installed`'); + expect(r.description).toContain('`param: "agent"`'); + }); + + it('POST /runs keeps the workspace_not_running example next to the new one', () => { + const ex = op('/runs', 'post').responses['409'].content['application/json'].examples; + expect(Object.keys(ex)).toEqual(['workspace_not_running', 'agent_not_installed']); + expect(ex.workspace_not_running.value.error.code).toBe('workspace_not_running'); + }); + + it('the validator accepts every agent and rejects near misses', () => { + for (const agent of AGENTS) { + expect(validate({ workspace: 'w', agent, prompt: 'x' }, sc.CreateRunRequest), agent).toEqual([]); + } + for (const agent of ['pi-gentle', 'gentle_shell', 'gentle-shell', 'Pi', 'pi_gentle ']) { + expect(validate({ workspace: 'w', agent, prompt: 'x' }, sc.CreateRunRequest), agent).not.toEqual([]); + } + }); +}); diff --git a/src/tests/layouts/runAgentsDocs.test.ts b/src/tests/layouts/runAgentsDocs.test.ts new file mode 100644 index 0000000..880d51b --- /dev/null +++ b/src/tests/layouts/runAgentsDocs.test.ts @@ -0,0 +1,171 @@ +import { describe, expect, test } from 'vitest'; +import { readFileSync } from 'node:fs'; +import { fileURLToPath } from 'node:url'; +import { dirname, resolve } from 'node:path'; +import openapi from '../../data/openapi.json'; +import { validate } from '../helpers/openapiValidate'; + +/** + * The run agents across the guides: Pi, Pi + Gentle Shell and Hermes. + * + * Facts from cloud-api #345 (0.8.258), cloud-ui #158 and nan-cli #47 (0.1.29): + * - three agent values, `pi`, `pi_gentle`, `hermes` (protocol.Agents), shown + * as "Pi", "Pi + Gentle Shell" and "Hermes" (runs.AgentName, cloud-ui + * agentName), in that display order (software.RunAgentIDs); + * - `pi_gentle` needs Pi and gentle-shell; runs and automations only accept + * an agent the workspace has installed, otherwise `409 agent_not_installed` + * with `param: "agent"` (CLI exit 4); + * - the workspace object lists them in `run_agents`; + * - `nan run --agent pi_gentle` needs nan-cli 0.1.29. + * + * EN and ES must say the same: same table rows, same values, same anchors. + */ +const here = dirname(fileURLToPath(import.meta.url)); +const src = resolve(here, '../..'); +const read = (p: string) => readFileSync(resolve(src, p), 'utf-8'); +const strip = (raw: string) => raw.replace(/^---[\s\S]*?\n---\n/, ''); + +const AGENTS: Array<[label: string, value: string]> = [ + ['Pi', 'pi'], + ['Pi + Gentle Shell', 'pi_gentle'], + ['Hermes', 'hermes'], +]; + +const pages = { + en: { dir: 'content/docs/', prefix: '', heading: '## Pick the agent', anchor: '#pick-the-agent' }, + es: { dir: 'content/docs-es/', prefix: '/es', heading: '## Elige el agente', anchor: '#elige-el-agente' }, +} as const; + +/** The rows of the first markdown table after `heading`, as trimmed cells. */ +function tableAfter(body: string, heading: string): string[][] { + const start = body.indexOf(`\n${heading}\n`); + expect(start, heading).toBeGreaterThan(-1); + const rest = body.slice(start + heading.length + 2); + const lines = rest.split('\n'); + const first = lines.findIndex((l) => l.startsWith('|')); + const rows: string[][] = []; + for (const line of lines.slice(first)) { + if (!line.startsWith('|')) break; + rows.push(line.replace(/\\\|/g, '\u0000').split('|').slice(1, -1).map((c) => c.trim().replace(/\u0000/g, '|'))); + } + return rows.slice(2); // header + separator +} + +/** The section that starts at `heading`, up to the next `## `. */ +function section(body: string, heading: string): string { + const start = body.indexOf(`\n${heading}\n`); + const end = body.indexOf('\n## ', start + heading.length + 2); + return body.slice(start, end === -1 ? undefined : end); +} + +describe.each(Object.entries(pages))('run agents in the guides (%s)', (locale, page) => { + const runs = strip(read(page.dir + 'runs.mdx')); + const flat = runs.replace(/\s+/g, ' '); + const runsLink = `(${page.prefix}/docs/runs${page.anchor})`; + + test('Pick the agent: one row per agent, with its portal label and value, in display order', () => { + const rows = tableAfter(runs, page.heading); + expect(rows.map((r) => [r[0], r[1]])).toEqual(AGENTS.map(([label, value]) => [`**${label}**`, `\`${value}\``])); + // pi_gentle needs both programs; Pi alone is not enough. + expect(rows[1][2]).toMatch(/^Pi (and|y) gentle-shell\./); + }); + + test('Pick the agent sits right after Before you start', () => { + const headings = [...runs.matchAll(/^## (.+)$/gm)].map((m) => m[1]); + expect(headings.indexOf(page.heading.slice(3))).toBe(1); + }); + + test('explains the installed-only rule: portal list, 409 with param agent, CLI exit 4, automations', () => { + const sec = section(runs, page.heading).replace(/\s+/g, ' '); + expect(sec).toContain('`409 agent_not_installed`'); + expect(sec).toMatch(/\((the error names the `agent` field|el error señala el campo `agent`)\)/); + expect(sec).toContain('**(not installed)**'); + expect(sec).toMatch(/`nan run` (exits with code|sale con código) 4/); + expect(sec).toContain('`run_agents`'); + expect(sec).toContain(`(${page.prefix}/docs/api#tag/workspaces)`); + }); + + test('the intro, the prerequisites and the form name all three agents and link the section', () => { + expect(flat).toMatch(/\(Pi, Pi \+ Gentle Shell (or|o) Hermes, (among the ones installed in that workspace|entre los que tengas instalados en ese workspace)\)/); + expect(flat).not.toMatch(/Pi (or|o) Hermes/); + const formRow = runs.split('\n').find((l) => l.startsWith('| **Agent** |'))!; + for (const [label] of AGENTS) expect(formRow).toContain(`**${label}**`); + expect(formRow).toContain(`](${page.anchor})`); + }); + + test('the CLI documents --agent pi|pi_gentle|hermes, its version and a pi_gentle example', () => { + const flagRow = runs.split('\n').find((l) => l.startsWith('| `--agent'))!; + expect(flagRow).toContain('`--agent pi\\|pi_gentle\\|hermes`'); + for (const [label, value] of AGENTS) expect(flagRow).toContain(`\`${value}\` (${label})`); + expect(runs).toContain('**0.1.29**'); + expect(runs).toMatch(/^nan run --ws develop --agent pi_gentle "/m); + }); + + test('the API 409 example is a valid error envelope and matches the reference', () => { + const block = /```json\n(\{\n {2}"error"[\s\S]*?\n\})\n```/.exec(runs); + expect(block).not.toBeNull(); + const err = JSON.parse(block![1]); + const spec = openapi as any; + const r409 = spec.paths['/runs'].post.responses['409'].content['application/json']; + expect(validate(err, r409.schema)).toEqual([]); + expect(err).toEqual(r409.examples.agent_not_installed.value); + expect(flat).toMatch(/`agent` (is|es) `pi`, `pi_gentle` (or|o) `hermes`/); + }); + + test('automations: prerequisites and the Agent row', () => { + const auto = strip(read(page.dir + 'automations.mdx')); + expect(auto).toContain(runsLink); + expect(auto).not.toMatch(/Pi (or|o) Hermes/); + const row = auto.split('\n').find((l) => l.startsWith('| **Agent** |'))!; + for (const [label] of AGENTS) expect(row).toContain(`**${label}**`); + expect(row).toContain('**(not installed)**'); + expect(row).toContain('`agent_not_installed`'); + // Short cell: the details live in Pick the agent. + expect(row.length).toBeLessThan(420); + expect(row).toContain(runsLink); + }); + + test('PR gatekeeper: the prerequisite names the installed agent and links Pick the agent', () => { + const pg = strip(read(page.dir + 'pr-gatekeeper.mdx')); + const line = pg.split('\n').find((l) => l.includes('Pi + Gentle Shell'))!; + expect(line).toBeDefined(); + expect(line).toContain(runsLink); + expect(line).toMatch(/(The example uses Pi|El ejemplo usa Pi)\.$/); + expect(pg).not.toMatch(/Pi (or|o) Hermes/); + }); + + test('workspaces: the run sentence names the three agents and gentle-shell links to Pick the agent', () => { + const ws = strip(read(page.dir + 'workspaces.mdx')); + const gentle = ws.split('\n').find((l) => l.startsWith('gentle-shell '))!; + expect(gentle).toContain('**Pi + Gentle Shell**'); + expect(gentle).toContain(runsLink); + expect(ws).toMatch(/\(Pi, Pi \+ Gentle Shell (or|o) Hermes\)/); + expect(ws).not.toMatch(/Pi (or|o) (a )?Hermes/); + }); + + test('no em-dashes in what this change touched', () => { + for (const f of ['runs.mdx', 'automations.mdx', 'pr-gatekeeper.mdx', 'workspaces.mdx']) { + const body = strip(read(page.dir + f)); + for (const line of body.split('\n').filter((l) => /pi_gentle|Pi \+ Gentle Shell|agent_not_installed/.test(l))) { + expect(line, `${locale} ${f}`).not.toContain('—'); + } + } + }); +}); + +describe('run agents: EN and ES say the same', () => { + const en = strip(read(pages.en.dir + 'runs.mdx')); + const es = strip(read(pages.es.dir + 'runs.mdx')); + + test('same agent table labels and values', () => { + const cols = (rows: string[][]) => rows.map((r) => [r[0], r[1]]); + expect(cols(tableAfter(es, pages.es.heading))).toEqual(cols(tableAfter(en, pages.en.heading))); + }); + + test('same --agent flag cell and the same 409 example', () => { + const flag = (b: string) => b.split('\n').find((l) => l.startsWith('| `--agent'))!.split('|')[1]; + expect(flag(es)).toBe(flag(en)); + const err = (b: string) => /```json\n(\{\n {2}"error"[\s\S]*?\n\})\n```/.exec(b)![1]; + expect(err(es)).toBe(err(en)); + }); +});