openvibers/openvibe.actor AGENTS.md

AGENTS.md

main 11.0 KB · 173 lines · markdown

1# AGENTS.md — OpenVibe.Actor
2
3## Project overview
4
5OpenVibe's own general agent and the router for agent work: a task in plain words is sorted (answer,
6lookup, research, live web search or code), placed on the best agent for it, run while its steps
7stream, checked by a model of another family, then delivered with its cost and the reason it went
8where it did. Node.js/Express, PostgreSQL (`ov_actor`), CommonJS, **no build step**.
9
10- Service id `actor`, port `4950`, origin `https://openvibe.actor`, unit `openvibe-actor.service`,
11 release root `/opt/openvibe.actor`; live on openvibe-ovh.
12- Design: plan T17 and ADR-044 (`openvibe-contracts`). Actor is also the front desk of the one agent
13 system: it can delegate coding to Codes, a robot to Bot and a machine's agent to Link.
14
15## What it owns
16
17- Tasks, their event logs and daily spend (`migrations/0001_tasks.sql`); tasks are kept 30 days, then
18 pruned with their events. Nothing stored is a credential, a key or a token.
19- The agent catalog and its human-typed rate cards; the routing policy over them (placement itself is
20 `openvibe-sdk/placement`).
21- An agent's **voice** as a file (`actor.voice@1`, `server/agents/voice.js`), its integrity hash and
22 public shares at `/voices/<id>`.
23- The pages (`/`, `/agents`, `/docs`, `/tasks`, `/tasks/:id`, `/updates`), the console
24 (`/console`), the task API, chat asks (`@actor` in Chat), triggers/cron, webhooks, agent mail,
25 connections (MCP/A2A), skills, memory, sharing, task artefacts and the `actor.task.*` events.
26- Request telemetry (`telemetry_samples`, 14 days; route templates only, never raw URLs).
27
28Not owned: coding runs (Codes), execution sandboxes/browsers/desktops (Run/Link), accounts, apps and
29grants (Network/Services), the tools (Tools), search (Search), the model providers' routing (AI when
30its router is on).
31
32## Who calls it, and whom it calls
33
34- Callers: people in the browser; apps, agents and services with a Network service token audience
35 `openvibe.actor` and the route's capability; a first-party `svc:` acting for a person with
36 `X-OV-Subject` (e.g. Watch's actor-task action); OpenVibe.Chat (`@actor <task>`).
37- Depends on (all base URLs in [server/config.js](server/config.js)):
38 - **Network** — OAuth2+PKCE (client `actor`), JWKS, account export/deletion pushes.
39 - **Search** (`ACTOR_SEARCH_URL`) — `search_openvibe` anonymous; `search_web` as Actor's principal
40 with `search.query.delegate` and `X-OV-Subject`, so paid steps count against the person.
41 - **Tools** (`ACTOR_TOOLS_URL`) — catalog, run API, page reader, as an anonymous caller.
42 - **Bot** (`ACTOR_BOT_URL`, default `http://127.0.0.1:4630`) — the person's robots and computers
43 (`bot.robot.*`, `bot.computer.*`), owner-only and only where the owner turned the switch on.
44 - **Events** (`ACTOR_EVENTS_URL`) — account-data subscriptions, `chat.message.created`.
45 - **Chat** (`ACTOR_CHAT_URL`, default `http://127.0.0.1:4400`) — asks and replies.
46 - **AI** (`ACTOR_AI_ROUTER=1` in production) — every model call is an `ai.complete` run with
47 `on_behalf_of` the task's person; each tier names an AI policy (`ACTOR_AI_MODEL_*`). With the
48 router off, a self-hosted Actor uses DeepSeek/OpenAI/a local server directly.
49 - PostgreSQL and Valkey; `pdftotext` for PDF attachments.
50
51## How a task runs
52
531. **Sort** — [server/agents/classify.js](server/agents/classify.js) (cheap model, or a free heuristic
54 in private mode).
552. **Route** — [server/agents/router.js](server/agents/router.js) over
56 [server/agents/catalog.js](server/agents/catalog.js): hard requirements first (capability, private
57 trust, health, budget), then the mode's objective (cheapest/balanced/best/fastest/private). The
58 result is a `platform.placement-result@1` stored as the explanation.
593. **Run** — [server/agents/adapters.js](server/agents/adapters.js); steps stream as events; model
60 calls and searches are metered at once, so a cancelled task still costs what it used.
614. **Check** — [server/agents/check.js](server/agents/check.js): a model of another family reads the
62 answer (DeepSeek ↔ OpenAI). In private mode the open model checks itself and the task says so. An
63 answer that cannot be checked is never delivered.
645. **Deliver or hand on** — [server/tasks/engine.js](server/tasks/engine.js): a pass returns the
65 result, cost and explanation; an error or failed check hands to the next eligible agent (at most
66 two), appending each placement to the explanation.
67
68## Repository map
69
70- **Entry:** [server/index.js](server/index.js) — `createApp()`, HTTP listen, status reconciliation;
71 `createLifecycle` stops tasks and leaves their rows open for checkpoint resume.
72- **App:** [server/app.js](server/app.js) — helmet/CSP, rate limits, `/auth`, `/internal/events`,
73 `/shared`, `/api/v1` (tasks, resources, fabric), `/api`, pages, console, chat/agent/memory pages.
74- **Config:** [server/config.js](server/config.js), [server/ai-catalog-config.js](server/ai-catalog-config.js).
75- **Database:** [server/db.js](server/db.js), [server/tasks/store.js](server/tasks/store.js); schema in
76 [migrations/](migrations/).
77- **Tasks:** [server/tasks/](server/tasks/) — `engine.js`, `create.js`, `stream.js`, `webhooks.js`.
78- **Agents/runtime:** [server/agents/](server/agents/), [server/runtime/](server/runtime/) (loop,
79 models, checkpoints, subagent, evidence, tools, stats, artifacts).
80- **Auth:** [server/auth/keys.js](server/auth/keys.js), [server/auth/sso.js](server/auth/sso.js),
81 [server/http/principal.js](server/http/principal.js).
82- **HTTP/pages:** [server/http/](server/http/) — `api.js`, `pages.js`, `chat-pages.js`,
83 `agent-pages.js`, `console-pages.js`, `memory-pages.js`.
84- **Integrations:** [server/chat/](server/chat/), [server/triggers/](server/triggers/),
85 [server/mail/](server/mail/), [server/connections/](server/connections/).
86- **Self-host:** [scripts/selfhost-init.js](scripts/selfhost-init.js), [docs/self-host.md](docs/self-host.md),
87 [docker-compose.selfhost.yml](docker-compose.selfhost.yml), [Dockerfile](Dockerfile).
88
89## Commands
90
91```bash
92npm install
93npm run dev # http://localhost:4950 (agents without keys are unavailable)
94npm start # node server/index.js
95npm test # every test/*.test.js on temp PGlite, mock Network/Events/Media
96npm run test:pg # same suite on PostgreSQL (ACTOR_TEST_STORE=pg)
97npm test -- tasks # filter by file name; --strict makes a skip fail
98npm run selfhost:init # owner subject, password hash, token, session secret
99ov test # the harness's sharded run of what the change reaches
100```
101
102## Auth and capabilities
103
104- A person acting for themself needs no capability. App/agent/service tokens for audience
105 `openvibe.actor` need the route's capability: `actor.task.create`, `actor.task.read`,
106 `actor.task.list`; `actor.agent.read` covers the public catalog/dry run.
107- A **first-party `svc:`** may act for a person named in `X-OV-Subject`; the task is the person's
108 (allowance, list, export, deletion) and records the service as `via`; it can read/list/cancel only
109 the tasks it started. A developer app or an agent may never name a person (`403 subject.not_delegated`).
110- Sessions are HttpOnly cookies from Network SSO; a FedCM assertion or an app/service token is never a
111 session. Never read identity from a body or a query.
112
113## Frontend loading model
114
115Server-rendered pages and forms, progressively enhanced by no-build scripts under
116[public/js/](public/js/); there is no SPA framework, bundler or transpiler. `openvibe-shared` supplies
117the Frame, shell, showcase and `/shared/*` (served from the pinned node_modules). The home page has
118size budgets (`test/home.test.js`, `test/asset-cache.test.js`); CSP is set in
119[server/app.js](server/app.js).
120
121## Deploy
122
123`sudo ovhost deploy actor` (git checkout at `/opt/openvibe.actor`, unit `openvibe-actor.service` on
124`127.0.0.1:4950`, env `/etc/openvibe/actor.env`, database `ov_actor` on the data role); nginx
125[deploy/nginx/openvibe.actor.conf](deploy/nginx/openvibe.actor.conf) installed with `ov-vhost-install`,
126with the task event stream unbuffered. Rollback is automatic when `/api/ready` fails. **A stop or a
127deploy marks running tasks `actor.task.interrupted`; they are never re-run behind the person's back.**
128
129## Tests
130
131`npm test` runs each `test/*.test.js` in its own process on PGlite with in-process Network/Events/
132Media mocks and stand-in providers. The important ones: `tasks.test.js` (end-to-end, routing, check,
133hand-off, private mode, budgets, idempotency, cancel, stream, visibility), `contracts.test.js`
134(released manifest/capabilities match the routes), `account-data.test.js`, `caller-limits.test.js`,
135`security-ssrf.test.js`, `security-secrets.test.js`, `security-session.test.js`. `ov test` runs the same.
136
137## Conventions
138
139- CommonJS, 4-space indent, single quotes, semicolons; factories with injectable config/db/clock/
140 fetch/log; domain logic in `server/tasks/`, `server/agents/`, `server/runtime/`, HTTP in `server/http/`.
141- Contracts and ids come from `openvibe-contracts`; errors are problem+json with stable codes and
142 request ids. No secret in a response, an event or a log.
143- Rate cards are typed in by a person, with the provider's page and the date checked; a model never
144 writes a price.
145- For self-hosted deployments the owner/secret check is deliberate; do not loosen `checkSelfHosted`.
146
147## Pitfalls
148
149- **Actor never fetches a URL from a task or a model.** Tools' own SSRF guard decides what a tool may
150 reach; the same applies to `read_page`. Keep outbound calls to configured bases.
151- **Private mode must not send the text outside OpenVibe** — including the classifier and the check.
152- **An unchecked answer is never delivered.** With the AI router the check is one family, and the
153 verdict says so; do not silently drop the check.
154- **`@actor` asks are public:** they may use public tools only, and the engine refuses a robot,
155 computer or coding task (`actor.task.not_public`). Limits: 5/hour per person, 60 overall.
156- **`X-OV-Subject` is only for first-party services**; apps/agents cannot delegate.
157- **Budgets are hard** and metered per call; a cancelled task still charges what it used, and the free
158 tier is checked before any paid call.
159- **Interrupted tasks are not re-run**; never "retry" a person's task automatically after a deploy.
160- **Self-hosted boot refuses** without `ACTOR_OWNER_SUBJECT`, `ACTOR_OWNER_PASSWORD_HASH`,
161 `ACTOR_OWNER_TOKEN` (32+) and `ACTOR_SESSION_SECRET` (32+).
162- **A shared voice's author is only a claim**; the integrity hash proves the file was not edited, not
163 who wrote it.
164
165## Documentation
166
167- [README.md](README.md) — the full product, agents, money, API, acceptance and threats.
168- [docs/one-engine.md](docs/one-engine.md) — the shared engine with Codes and Run.
169- [docs/agent-runs.md](docs/agent-runs.md), [docs/task-view.md](docs/task-view.md).
170- [docs/personal-actor.md](docs/personal-actor.md), [docs/self-host.md](docs/self-host.md),
171 [docs/work-fabric.md](docs/work-fabric.md).
172- ADR-044 in OpenVibe.Contracts is the binding design.
173