@flame0510/project-aether 1.2.0 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -1
- package/app/agents/ModelSection.tsx +313 -0
- package/app/agents/PageClient.tsx +83 -4
- package/app/agents/create/page.tsx +8 -21
- package/app/api/agents/[id]/model/route.ts +113 -0
- package/app/api/agents/[id]/recreate/route.ts +10 -34
- package/app/api/agents/[id]/route.ts +10 -29
- package/app/api/agents/create/route.ts +59 -57
- package/app/api/agents/models-summary/route.ts +163 -0
- package/app/api/assistant/route.ts +36 -15
- package/app/api/gateway/agent/route.ts +23 -6
- package/app/api/gateway/provider/keys.ts +13 -1
- package/app/api/gateway/provider/route.ts +43 -12
- package/app/api/gateway/sync.ts +248 -72
- package/app/api/models/route.ts +28 -34
- package/app/api/provider/auth.ts +65 -0
- package/app/api/provider/upstream.ts +9 -2
- package/app/api/provider/v1/chat/completions/route.ts +22 -16
- package/app/api/provider/v1/models/route.ts +26 -133
- package/app/components/PulseChat.tsx +25 -39
- package/app/components/ui/RemoveButton.tsx +46 -0
- package/app/components/ui/Select.tsx +3 -2
- package/app/components/ui/index.ts +1 -0
- package/app/credentials/PageClient.tsx +2 -2
- package/app/gateway/PageClient.tsx +257 -673
- package/app/globals.css +8 -0
- package/app/lib/models-context.tsx +43 -7
- package/app/wizard/useWizard.ts +6 -1
- package/bin/rev4a.js +73 -9
- package/docs/ARCHITECTURE.md +16 -4
- package/docs/FRONTEND-ARCHITECTURE.md +24 -2
- package/docs/REV4A.md +40 -17
- package/docs/dev/API-REFERENCE.md +170 -79
- package/docs/dev/GATEWAY.md +231 -89
- package/docs/dev/PROVIDERS.md +26 -13
- package/docs/rag/DATA-FRESHNESS.md +57 -28
- package/docs/rag/GLOSSARY.md +16 -14
- package/docs/rag/REV4A-OVERVIEW.md +23 -24
- package/docs/rag/WHAT-I-CAN-ANSWER.md +5 -7
- package/instrumentation.ts +9 -1
- package/lib/agent-readiness.ts +110 -0
- package/lib/channelManager.ts +64 -22
- package/lib/container-file.ts +27 -0
- package/lib/model-catalogue.ts +140 -27
- package/lib/rev4a-paths.ts +0 -21
- package/model-pricing.json +118 -110
- package/models.config.json +27 -12
- package/package.json +1 -1
- package/app/api/gateway/route.ts +0 -191
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Data Freshness in Rev4a
|
|
2
2
|
|
|
3
|
-
> **Last updated:** 2026-
|
|
3
|
+
> **Last updated:** 2026-09-13
|
|
4
4
|
|
|
5
5
|
Understanding how current the data in Rev4a is — and what is truly real-time vs. periodically updated.
|
|
6
6
|
|
|
@@ -8,47 +8,60 @@ Understanding how current the data in Rev4a is — and what is truly real-time v
|
|
|
8
8
|
|
|
9
9
|
## Session data
|
|
10
10
|
|
|
11
|
-
**Update frequency:** every
|
|
11
|
+
**Update frequency:** every 30 seconds
|
|
12
12
|
|
|
13
|
-
The daemon polls `openclaw sessions --json --all-agents` on
|
|
14
|
-
|
|
15
|
-
|
|
13
|
+
The daemon polls `openclaw sessions --json --all-agents` on a fixed 30-second
|
|
14
|
+
timer. Session statuses, token counts, and costs are therefore up to 30 seconds
|
|
15
|
+
behind reality.
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
The code declares a shorter 15-second interval for when a session is `working`,
|
|
18
|
+
but nothing uses it — the timer is always 30 seconds. Do not tell a user the
|
|
19
|
+
dashboard speeds up during active work.
|
|
18
20
|
|
|
19
|
-
**What this means for PULSE:** if you ask "is Argus working right now?", the
|
|
21
|
+
**What this means for PULSE:** if you ask "is Argus working right now?", the
|
|
22
|
+
answer reflects data that is up to 30 seconds old.
|
|
20
23
|
|
|
21
24
|
---
|
|
22
25
|
|
|
23
26
|
## Event feed
|
|
24
27
|
|
|
25
|
-
**Update frequency:**
|
|
28
|
+
**Update frequency:** every 5 seconds (via SSE stream)
|
|
26
29
|
|
|
27
|
-
The `/api/stream` endpoint pushes updates to the dashboard every
|
|
30
|
+
The `/api/stream` endpoint pushes updates to the dashboard every 5 seconds. It
|
|
31
|
+
sends exactly three things:
|
|
28
32
|
- New events (spawn, complete, error, tool_call)
|
|
29
|
-
-
|
|
33
|
+
- The session list
|
|
30
34
|
- Today's cost total
|
|
31
|
-
- Lineage data
|
|
32
35
|
|
|
33
|
-
|
|
36
|
+
Lineage is **not** pushed over the stream; the Lineage page fetches it itself.
|
|
37
|
+
|
|
38
|
+
The event feed is as close to real-time as Rev4a gets. However, events are
|
|
39
|
+
generated by the daemon's poll cycle, so an event that just happened may take up
|
|
40
|
+
to 30 seconds to appear, plus up to 5 more to reach the browser.
|
|
34
41
|
|
|
35
42
|
---
|
|
36
43
|
|
|
37
44
|
## System metrics (CPU, RAM, disk)
|
|
38
45
|
|
|
39
|
-
**Update frequency:** every daemon poll cycle (
|
|
46
|
+
**Update frequency:** every daemon poll cycle (30 seconds)
|
|
47
|
+
|
|
48
|
+
**Retention:** 30 days (older rows are pruned automatically each cycle)
|
|
40
49
|
|
|
41
|
-
|
|
50
|
+
These metrics are collected and stored, and served by `GET /api/metrics`. They
|
|
51
|
+
are **not** shown on the dashboard — see the System Health note below.
|
|
42
52
|
|
|
43
53
|
---
|
|
44
54
|
|
|
45
55
|
## Cost totals
|
|
46
56
|
|
|
47
|
-
**Update frequency:** every
|
|
57
|
+
**Update frequency:** every 30 seconds (recalculated from sessions on each API call)
|
|
48
58
|
|
|
49
|
-
The cost figures shown in the dashboard are always computed fresh from the
|
|
59
|
+
The cost figures shown in the dashboard are always computed fresh from the
|
|
60
|
+
database on each page load or SSE push. They are not cached.
|
|
50
61
|
|
|
51
|
-
**Cost override:**
|
|
62
|
+
**Cost override:** `/api/cost-override` exists, but no page in the dashboard
|
|
63
|
+
reads or writes it. There is no UI for setting one, so do not direct a user to
|
|
64
|
+
"the cost override screen".
|
|
52
65
|
|
|
53
66
|
---
|
|
54
67
|
|
|
@@ -66,16 +79,25 @@ Cron status (last run, next run) is fetched directly from the OpenClaw gateway e
|
|
|
66
79
|
|
|
67
80
|
**Update frequency:** on demand (read from `openclaw.json` on each request)
|
|
68
81
|
|
|
69
|
-
The agents list, Telegram accounts, and bindings are read directly from
|
|
82
|
+
The agents list, Telegram accounts, and bindings are read directly from
|
|
83
|
+
`openclaw.json` each time they are requested.
|
|
84
|
+
|
|
85
|
+
`GET /api/agents-config` serves this, but no page calls it — there is no "Agents
|
|
86
|
+
Config page". Per-agent settings are reached from the agent's detail panel on the
|
|
87
|
+
Agents page.
|
|
70
88
|
|
|
71
89
|
## Agent base image
|
|
72
90
|
|
|
73
|
-
**Update frequency:** polled every 2 seconds
|
|
91
|
+
**Update frequency:** polled every 2 seconds, continuously
|
|
74
92
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
93
|
+
`GET /api/agents/image-status` compares the local image digest with the remote
|
|
94
|
+
registry digest on ghcr.io. The banner polls it every 2 seconds for as long as the
|
|
95
|
+
page is open, not only while something is running.
|
|
96
|
+
|
|
97
|
+
The operation the banner triggers is a **`docker pull`**, not a build. While it
|
|
98
|
+
runs the banner shows an indeterminate animated bar: there is no percentage, no
|
|
99
|
+
byte count, and no log output in the UI. A log is written server-side to
|
|
100
|
+
`/tmp/rev4a-download-<timestamp>.log`, but nothing in the dashboard reads it.
|
|
79
101
|
|
|
80
102
|
---
|
|
81
103
|
|
|
@@ -83,7 +105,9 @@ via polling `GET /api/agents/image-status` and reading logs from
|
|
|
83
105
|
|
|
84
106
|
**Update frequency:** on demand (files read from disk on each request)
|
|
85
107
|
|
|
86
|
-
Memory files
|
|
108
|
+
Memory files are read from disk each time they are requested. There is no caching.
|
|
109
|
+
|
|
110
|
+
The tracked set is `USER.md`, `MEMORY.md`, `AGENTS.md`, `SOUL.md`, `HEARTBEAT.md`.
|
|
87
111
|
|
|
88
112
|
---
|
|
89
113
|
|
|
@@ -91,7 +115,7 @@ Memory files (MEMORY.md, etc.) are read from disk each time they are requested.
|
|
|
91
115
|
|
|
92
116
|
| Data | Why it may be stale |
|
|
93
117
|
|---|---|
|
|
94
|
-
|
|
|
118
|
+
| Cron sessions older than 7 days | The daemon prunes cron session rows after 7 days |
|
|
95
119
|
| Sessions never polled by daemon | If the daemon was down, sessions from that window are missing |
|
|
96
120
|
| Tool calls for a session | Only captured if the OpenClaw session export includes them |
|
|
97
121
|
| Costs before Rev4a was installed | Historical data before the first daemon run is not available |
|
|
@@ -100,10 +124,15 @@ Memory files (MEMORY.md, etc.) are read from disk each time they are requested.
|
|
|
100
124
|
|
|
101
125
|
## How to check if data is fresh
|
|
102
126
|
|
|
103
|
-
|
|
127
|
+
System health is a card on the **Dashboard**, not a page of its own. Its checks
|
|
128
|
+
cover session runtime, ingestion freshness, errors in the last 24 hours, today's
|
|
129
|
+
usage-based cost, and cron jobs. It reports no CPU, RAM, or disk figures.
|
|
104
130
|
|
|
105
|
-
|
|
131
|
+
To check directly on the server:
|
|
106
132
|
```bash
|
|
107
|
-
sqlite3 data/events.db \
|
|
133
|
+
sqlite3 ~/.config/rev4a/data/events.db \
|
|
108
134
|
"SELECT datetime(MAX(ts)/1000, 'unixepoch', 'localtime') FROM events"
|
|
109
135
|
```
|
|
136
|
+
|
|
137
|
+
The database lives under the Rev4a data directory, which defaults to
|
|
138
|
+
`~/.config/rev4a/` and moves with `REV4A_DATA_DIR`. It is not inside the repo.
|
package/docs/rag/GLOSSARY.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Rev4a Glossary
|
|
2
2
|
|
|
3
|
-
> **Last updated:** 2026-
|
|
3
|
+
> **Last updated:** 2026-09-14
|
|
4
4
|
|
|
5
5
|
Terms you'll encounter while using the Rev4a dashboard.
|
|
6
6
|
|
|
@@ -10,16 +10,16 @@ Terms you'll encounter while using the Rev4a dashboard.
|
|
|
10
10
|
An AI assistant configured with a specific role, AI model, and workspace. Each agent has a name (e.g. "Argus") and can run multiple sessions over time. Agents are deployed as Docker containers with persistent data volumes.
|
|
11
11
|
|
|
12
12
|
## Agent Template
|
|
13
|
-
A pre-built configuration used when creating a new agent.
|
|
13
|
+
A pre-built configuration used when creating a new agent. Each template ships its own set of files. Atlas has AGENTS.md, SOUL.md, IDENTITY.md, TOOLS.md, MEMORY.md and HEARTBEAT.md; Argus and Prometheus have the same minus HEARTBEAT.md; Custom has only AGENTS.md, MEMORY.md and BOOTSTRAP.md. Located in `agent-templates/`. Available templates: **Atlas** (general dev), **Argus** (security/audit), **Prometheus** (client-facing), and **Custom** (blank slate — bootstraps identity interactively on first run).
|
|
14
14
|
|
|
15
15
|
## Badge
|
|
16
|
-
A small inline label component used for status indicators and channel chips.
|
|
16
|
+
A small inline label component used for status indicators and channel chips. Six tones: neutral, accent, success, warning, danger, info. Used for the Telegram chip on the agent list and pairing status in the Channel Manager. There is no WhatsApp support anywhere in Rev4a.
|
|
17
17
|
|
|
18
18
|
## Backup (Agent)
|
|
19
19
|
A compressed archive (`.tar.gz`) of an agent's persistent volume (`/root/`). Backups exclude the npm cache to keep sizes small (~1.5 MB). Stored in the `rev4a-backups` Docker volume. Used for restore operations and auto-created before each recreate.
|
|
20
20
|
|
|
21
21
|
## Container
|
|
22
|
-
A Docker container running on the server. Each agent runs in its own container. The Containers page shows all containers, including infrastructure ones
|
|
22
|
+
A Docker container running on the server. Each agent runs in its own container. The Containers page shows all containers, including infrastructure ones such as databases and reverse proxies, with a web terminal link. It shows no CPU or memory metrics.
|
|
23
23
|
|
|
24
24
|
## Channel
|
|
25
25
|
A communication channel (Telegram) configured on an agent. Channels allow users to send DMs to the agent via messaging apps. The Channel Manager modal lets you connect/disconnect Telegram and manage pairings (approve/reject senders).
|
|
@@ -31,7 +31,7 @@ Modal in the agent detail panel for configuring Telegram channels. Connect with
|
|
|
31
31
|
The estimated cost of an agent session in USD, calculated from tokens used and per-model pricing. Rev4a shows cost by today, 7 days, 30 days, all-time, and by model.
|
|
32
32
|
|
|
33
33
|
## Cost Override
|
|
34
|
-
A manual correction for a month's total cost
|
|
34
|
+
A manual correction for a month's total cost, stored through `/api/cost-override`. There is currently no screen for it: no page in the dashboard reads or writes an override, so it can only be set by calling the API directly.
|
|
35
35
|
|
|
36
36
|
## Credential
|
|
37
37
|
A third-party service token (GitHub PAT, Trello API key, Vercel token, Supabase access key, Notion API token) stored in the Rev4a vault. A credential is installed into a container only when the user syncs it, and removed on de-sync; any credential can be synced to any container. Every reveal is written to an audit table that no route or view reads.
|
|
@@ -40,25 +40,25 @@ A third-party service token (GitHub PAT, Trello API key, Vercel token, Supabase
|
|
|
40
40
|
A scheduled task that runs an agent automatically at a fixed time (e.g. every night at 3:15 AM). Configured with standard cron syntax. Jobs can be enabled/disabled per entry.
|
|
41
41
|
|
|
42
42
|
## Dashboard
|
|
43
|
-
The main page of Rev4a (`/`). Shows live sessions, cost summary,
|
|
43
|
+
The main page of Rev4a (`/`). Shows live sessions, cost summary, health cards for Rev4a's runtime, cron and lineage, and a real-time event feed. It shows no CPU, RAM, disk or load average, and there is no setup banner: an incomplete wizard redirects to `/wizard`.
|
|
44
44
|
|
|
45
45
|
## Event
|
|
46
|
-
A lifecycle occurrence for a session: spawned, completed, errored, or a tool call made. Events appear in the live feed on the Dashboard and are pushed via SSE every
|
|
46
|
+
A lifecycle occurrence for a session: spawned, completed, errored, or a tool call made. Events appear in the live feed on the Dashboard and are pushed via SSE every 5 seconds.
|
|
47
47
|
|
|
48
48
|
## Gateway
|
|
49
|
-
The routing layer that connects agents to AI providers. The Gateway page
|
|
49
|
+
The routing layer that connects agents to AI providers. The Gateway page manages provider API keys and the model catalogue, and pushes that catalogue to every agent. Which model a given agent runs is set elsewhere, in the Model section of that agent's detail panel.
|
|
50
50
|
|
|
51
51
|
## Lineage
|
|
52
|
-
The parent-child relationship tree between sessions. When an agent spawns another agent to do work, that relationship is shown as an interactive graph on the Lineage page. Configurable time periods: 1d, 7d, 30d.
|
|
52
|
+
The parent-child relationship tree between sessions. When an agent spawns another agent to do work, that relationship is shown as an interactive graph on the Lineage page. Configurable time periods: 1d, 3d, 7d, 15d, 30d and all.
|
|
53
53
|
|
|
54
54
|
## Memory
|
|
55
|
-
Files that store an agent's persistent knowledge and personality
|
|
55
|
+
Files that store an agent's persistent knowledge and personality. The tracked set is USER.md, MEMORY.md, AGENTS.md, SOUL.md and HEARTBEAT.md. Editing them changes how an agent behaves. The Memory page shows these with size and budget tracking. A template may also ship IDENTITY.md or TOOLS.md, which are not tracked.
|
|
56
56
|
|
|
57
57
|
## Model
|
|
58
|
-
The AI model used by an agent or session. Examples: GPT-5.4, Claude Sonnet
|
|
58
|
+
The AI model used by an agent or session. Examples: GPT-5.4, Claude Sonnet 5, DeepSeek Flash. Models are linked to specific providers. The Gateway page controls which models are enabled for the whole deployment; the agent detail panel sets which one a given agent runs, plus its fallbacks. A model whose name the provider has retired is tagged `deprecated` on the agent card and in that panel. The Gateway page does not show it.
|
|
59
59
|
|
|
60
60
|
## First-run wizard
|
|
61
|
-
First-time setup wizard at `/wizard`. Steps: Welcome → Providers → First Agent → Ready. Completion is recorded in `wizard.json` under the data directory. Always accessible from the sidebar
|
|
61
|
+
First-time setup wizard at `/wizard`. Steps: Welcome → Providers → First Agent → Ready. Completion is recorded in `wizard.json` under the data directory. Always accessible from the sidebar. The mobile bottom navigation has five entries — Home, Agents, Workspace, Gateway, Containers — and the wizard is not one of them.
|
|
62
62
|
|
|
63
63
|
## Plugin
|
|
64
64
|
An extension that adds new tools and capabilities to agents. Plugins are installed in the workspace and appear in the Plugins page with enable/disable toggles.
|
|
@@ -119,7 +119,9 @@ Optional reverse proxy for routing web traffic to agent containers. If configure
|
|
|
119
119
|
Rev4a's store for third-party service tokens (GitHub, Trello, Vercel, Supabase, Notion). Provider API keys live separately in `provider-keys.json`, managed from the Gateway page. Supports reveal (written to an audit table no route or view reads), sync to and from containers, and live detection of which credentials are actually installed. Secrets are stored unencrypted.
|
|
120
120
|
|
|
121
121
|
## Workspace
|
|
122
|
-
The directory where an agent's operational files live (config, memory files, skills, plugins, scripts). The Workspace page lets you browse, view, and edit files across the
|
|
122
|
+
The directory where an agent's operational files live (config, memory files, skills, plugins, scripts). The Workspace page lets you browse, view, and edit files across the host workspace, which appears as `Local`, and all agent containers.
|
|
123
123
|
|
|
124
124
|
## System Health
|
|
125
|
-
|
|
125
|
+
A card on the Dashboard reporting whether Rev4a itself is working, not the hardware it runs on. `/api/system-health` returns checks for session runtime, feed ingestion freshness, errors in the last 24 hours, today's usage-based cost, and cron jobs, each with a health of `ok`, `warning` or `error`, plus a list of recommendations.
|
|
126
|
+
|
|
127
|
+
It reports no CPU, RAM, disk or load average. Those metrics are collected by the daemon and stored for 30 days, and are served by `/api/metrics`, but no page displays them.
|
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
# What is Rev4a?
|
|
2
2
|
|
|
3
|
-
> **Last updated:** 2026-09-
|
|
3
|
+
> **Last updated:** 2026-09-13
|
|
4
4
|
|
|
5
5
|
Rev4a is the control panel for your AI agent infrastructure. It shows you everything your agents are doing, how much they cost, and whether the system is healthy — all in one dashboard.
|
|
6
6
|
|
|
7
7
|
## What can you do in Rev4a?
|
|
8
8
|
|
|
9
9
|
### Dashboard (`/`)
|
|
10
|
-
The main page. See live sessions (who's working right now), cost summary
|
|
10
|
+
The main page. See live sessions (who's working right now), cost summary, an overall health indicator, and a real-time event feed. Click any session row to open the Session Drawer and see every tool call the agent made.
|
|
11
|
+
|
|
12
|
+
The health cards cover Rev4a's own runtime, cron and watchdog, and lineage. They do **not** show CPU, RAM, disk, or load average: those metrics are collected and stored, but no page displays them. There is no wizard banner on the dashboard; an incomplete setup redirects to `/wizard` instead.
|
|
11
13
|
|
|
12
14
|
### First-run wizard (`/wizard`)
|
|
13
15
|
First-run setup wizard that guides new users through configuration. Four steps:
|
|
@@ -16,7 +18,7 @@ First-run setup wizard that guides new users through configuration. Four steps:
|
|
|
16
18
|
3. **First Agent** — understand how agents work and what you need to create one
|
|
17
19
|
4. **Ready** — site map of key sections with quick links
|
|
18
20
|
|
|
19
|
-
The wizard is always accessible from the sidebar.
|
|
21
|
+
The wizard is always accessible from the sidebar. There is no progress badge on that entry and no banner elsewhere.
|
|
20
22
|
|
|
21
23
|
### Agents (`/agents`)
|
|
22
24
|
Manage Docker containers running OpenClaw agents. See which agents are running, stopped, or errored. Filter by status, view agent details (environment variables, ports, auth tokens, backups, Telegram channels), and create new agents. Each agent card shows its template, status, TG channel chip, and a direct link to its Control UI.
|
|
@@ -32,34 +34,29 @@ Manage Docker containers running OpenClaw agents. See which agents are running,
|
|
|
32
34
|
|
|
33
35
|
The agent list also shows a compact TG chip next to each agent name (green = connected, hidden if not configured).
|
|
34
36
|
|
|
35
|
-
The Agents page includes a banner for the **agent base image**
|
|
36
|
-
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
the build modal
|
|
40
|
-
|
|
41
|
-
The build modal shows live Docker build logs (polled every 2s). You can:
|
|
42
|
-
- **Run in Background** — close the modal, build continues on the server
|
|
43
|
-
- **Abort** — kill the build process; the existing image stays intact
|
|
44
|
-
- **Refresh / re-enter** — opening the modal again resumes log streaming
|
|
45
|
-
from the last seen line
|
|
37
|
+
The Agents page includes a banner for the **agent base image**
|
|
38
|
+
(`openclaw-agent-base:latest`). It is hidden when the image is present and current.
|
|
39
|
+
When the image is missing or outdated it appears with one button, labelled
|
|
40
|
+
**Download Image** or **Download Update**.
|
|
46
41
|
|
|
47
|
-
|
|
48
|
-
|
|
42
|
+
That button runs a `docker pull` from the registry. It is a download, not a build.
|
|
43
|
+
While it runs the banner shows an indeterminate animated bar with no percentage and
|
|
44
|
+
no log output; there is no modal, no "View Progress", no "Run in Background", and
|
|
45
|
+
no way to abort from the UI. Status is polled every 2 seconds.
|
|
49
46
|
|
|
50
47
|
### Create Agent (`/agents/create`)
|
|
51
48
|
Wizard to spin up a new agent. Steps: choose a template (Prometheus, Argus, Atlas, etc.), name your agent, pick a model, set an optional port range (default: auto-assigned 10-port block, e.g. 3700-3709). The agent is created as a Docker container with a persistent volume — all config, workspace files, and credentials survive container restarts.
|
|
52
49
|
|
|
53
50
|
### Lineage (`/lineage?period=7d`)
|
|
54
|
-
Interactive graph showing session family trees — which agent spawned which child agent, across configurable time periods
|
|
51
|
+
Interactive graph showing session family trees — which agent spawned which child agent, across configurable time periods: 1d, 3d, 7d, 15d, 30d, and all. Click any node to inspect. Includes a live feed side panel.
|
|
55
52
|
|
|
56
53
|
### Gateway (`/gateway`)
|
|
57
|
-
|
|
58
|
-
- **Provider Sync** — reads the model catalogue from `models.config.json`, discovers which providers have API keys, and syncs the full model list to every agent container
|
|
59
|
-
- **Agent Model Config** — set each agent's primary model and fallback models via `PUT /api/gateway/agent`
|
|
54
|
+
Connects agents to AI providers. Add and remove provider API keys, enable or disable individual models in the catalogue read from `models.config.json`, and push the resulting model list to every agent container. It does not assign models to agents: choosing which model an agent runs is done per agent, in the Model section of the agent's detail panel on the Agents page.
|
|
60
55
|
|
|
61
56
|
### Containers (`/containers`)
|
|
62
|
-
Full list of all Docker containers on the server.
|
|
57
|
+
Full list of all Docker containers on the server, including stopped ones. Each row shows name, status, image, IP, ports, and an agent pill where applicable. Click "Terminal" to open an interactive shell into that container.
|
|
58
|
+
|
|
59
|
+
The page shows no CPU or memory figures and has no start, stop, or restart buttons: it is read-only apart from the terminal link. Container lifecycle is managed from the Agents page.
|
|
63
60
|
|
|
64
61
|
### Container Terminal (`/containers/terminal/[id]`)
|
|
65
62
|
Live web terminal into a Docker container. Run commands, inspect files, debug issues — like SSH but in the browser.
|
|
@@ -74,13 +71,15 @@ Manage third-party service credentials: GitHub, Trello, Vercel, Supabase, Notion
|
|
|
74
71
|
Scheduled tasks auto-discovered from the host OpenClaw gateway (via `openclaw cron list --json`) and every running Docker container with the `AGENT_ID` label (via `docker exec openclaw cron list --json`). Filter jobs by agent using the toolbar tabs. Each job shows name, description, cron expression, next run, last run, and last status. Toggle on/off per job. The run history panel shows recent cron run executions for the selected job, loaded on demand from the container's gateway.
|
|
75
72
|
|
|
76
73
|
### Memory / Context (`/memory`)
|
|
77
|
-
Browse agent memory files
|
|
74
|
+
Browse agent memory files. The tracked set is USER.md, MEMORY.md, AGENTS.md, SOUL.md and HEARTBEAT.md. These files define how agents behave, what they know, and their personality. Also shows total memory budget vs usage.
|
|
75
|
+
|
|
76
|
+
A template may ship other files, such as IDENTITY.md or TOOLS.md, but those are not part of the tracked context set.
|
|
78
77
|
|
|
79
78
|
### Config (`/config`)
|
|
80
79
|
Rev4a application settings. View and modify environment variables (password, tokens, secrets), restart the server. Changes are persisted to `.env`, synced to systemd, and a daemon-reload is triggered.
|
|
81
80
|
|
|
82
81
|
### Tools (`/tools`)
|
|
83
|
-
Catalog of
|
|
82
|
+
Catalog of the OpenClaw built-in tools available to agents, grouped by area: file and code, shell, web, messaging, sessions, media, and others. The list is curated in the app, not discovered at runtime, so it covers built-ins only and does not enumerate MCP servers or plugin tools. The page also holds audio/TTS and timezone settings.
|
|
84
83
|
|
|
85
84
|
### Plugins (`/plugins`)
|
|
86
85
|
Browse installed OpenClaw plugins. Enable/disable toggle per plugin. Plugins extend agent capabilities with new tools and integrations.
|
|
@@ -101,7 +100,7 @@ Password-protected access to the dashboard. Enter the Rev4a password to authenti
|
|
|
101
100
|
|
|
102
101
|
## What about Pulse?
|
|
103
102
|
|
|
104
|
-
Pulse is the AI assistant embedded in Rev4a. She appears as a floating chat button in the bottom-right corner
|
|
103
|
+
Pulse is the AI assistant embedded in Rev4a. She appears as a floating chat button in the bottom-right corner, on every page except login, setup and the first-run wizard. She can:
|
|
105
104
|
|
|
106
105
|
- Explain what any page does
|
|
107
106
|
- Guide you through features ("how do I create an agent?")
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# What PULSE Can Answer
|
|
2
2
|
|
|
3
|
-
> **Last updated:** 2026-
|
|
3
|
+
> **Last updated:** 2026-09-14
|
|
4
4
|
|
|
5
5
|
PULSE is the in-dashboard AI concierge for Rev4a. This document defines what she can and cannot answer.
|
|
6
6
|
|
|
@@ -12,7 +12,7 @@ PULSE is the in-dashboard AI concierge for Rev4a. This document defines what she
|
|
|
12
12
|
- "How do I get to the container list?"
|
|
13
13
|
- "Where can I see the first-run wizard?"
|
|
14
14
|
- "Is there a page for cron jobs?"
|
|
15
|
-
- "Where can I see my AI providers?" — The Gateway page (`/gateway`) shows providers
|
|
15
|
+
- "Where can I see my AI providers?" — The Gateway page (`/gateway`) shows providers and the model catalogue. Which model an agent runs is on the Agents page, in that agent's detail panel.
|
|
16
16
|
- "How do I create a new agent?"
|
|
17
17
|
- "What templates are available?"
|
|
18
18
|
- "What is the Custom template?"
|
|
@@ -52,10 +52,10 @@ PULSE is the in-dashboard AI concierge for Rev4a. This document defines what she
|
|
|
52
52
|
- "How do I delete an agent?"
|
|
53
53
|
- "How do I restart the Rev4a server?"
|
|
54
54
|
- "How do I change environment variables?"
|
|
55
|
-
- "How do I add a cost override?"
|
|
55
|
+
- "How do I add a cost override?" — There is no screen for it. The endpoint exists, but nothing in the dashboard calls it.
|
|
56
56
|
- "How do I see the session lineage?"
|
|
57
57
|
- "How do I log in to an agent's control UI?"
|
|
58
|
-
- "How do I register a new provider via OAuth?"
|
|
58
|
+
- "How do I register a new provider via OAuth?" — The endpoint exists but no page uses it. Providers are added by pasting an API key on the Gateway page.
|
|
59
59
|
- "How do I manage agent model fallbacks?"
|
|
60
60
|
- "How do I connect an agent to Telegram?"
|
|
61
61
|
- "How do I approve a pairing code?"
|
|
@@ -97,9 +97,7 @@ PULSE is the in-dashboard AI concierge for Rev4a. This document defines what she
|
|
|
97
97
|
- "What does the yellow diamond next to an agent mean?"
|
|
98
98
|
- "Why is the agent base image banner showing?"
|
|
99
99
|
- "How do I fix 'image is outdated'?"
|
|
100
|
-
- "
|
|
101
|
-
- "Can I close the build modal while it's running?"
|
|
102
|
-
- "How do agent image rebuilds work?"
|
|
100
|
+
- "How do agent image updates work?" — The banner on the Agents page runs a `docker pull`. It is a download, not a build: there is no modal, no live log, and no way to abort from the UI.
|
|
103
101
|
- "Why is my Telegram bot not connecting?"
|
|
104
102
|
- "Why can't I approve a pairing code?"
|
|
105
103
|
- "Why is the TG badge missing from my agent?"
|
package/instrumentation.ts
CHANGED
|
@@ -9,7 +9,15 @@ export async function register() {
|
|
|
9
9
|
if (process.env.NEXT_RUNTIME === 'nodejs') {
|
|
10
10
|
try {
|
|
11
11
|
const { syncAllAgents } = await import('./app/api/gateway/sync');
|
|
12
|
-
syncAllAgents
|
|
12
|
+
// The outcome used to be discarded. syncAllAgents reports failures instead of
|
|
13
|
+
// throwing, so the catch below never saw a sync that reached no container —
|
|
14
|
+
// Docker not yet up at boot left the fleet stale with nothing in the log.
|
|
15
|
+
const outcome = syncAllAgents();
|
|
16
|
+
if (outcome.ok) {
|
|
17
|
+
console.log(`[rev4a] Startup sync: ${outcome.summary}`);
|
|
18
|
+
} else {
|
|
19
|
+
console.warn(`[rev4a] Startup sync incomplete: ${outcome.summary} Run Sync All Agents once the cause is fixed.`);
|
|
20
|
+
}
|
|
13
21
|
} catch {
|
|
14
22
|
// Best-effort — don't block startup if sync fails
|
|
15
23
|
console.warn('[rev4a] Failed to sync agents on startup');
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Waiting for an agent's Gateway to finish starting.
|
|
3
|
+
*
|
|
4
|
+
* **Which endpoint.** OpenClaw exposes three, answering three different questions:
|
|
5
|
+
*
|
|
6
|
+
* /health, /healthz the HTTP server is listening
|
|
7
|
+
* /startupz startup finished and the Gateway is not draining
|
|
8
|
+
* /readyz the above, plus configured channels pass deep checks
|
|
9
|
+
*
|
|
10
|
+
* `/startupz` is the one callers need: the create route configures the agent as
|
|
11
|
+
* soon as this returns. A Gateway that refused readiness — for instance one that
|
|
12
|
+
* found a legacy session store and wants `doctor --fix` — still answers 200 on
|
|
13
|
+
* `/health`. `/readyz` would fail on an expired channel token, and the create
|
|
14
|
+
* route deletes the container when this probe times out, so a false negative
|
|
15
|
+
* there destroys a healthy agent over an unrelated channel.
|
|
16
|
+
*
|
|
17
|
+
* **Two OpenClaw majors.** `/startupz` exists from 9.x: 503 while starting or
|
|
18
|
+
* draining, 200 with `{"status":"started"}` when done. The 2026.7.1-2 image that
|
|
19
|
+
* `openclaw-agent-base` currently pins has no `/startupz`; its Gateway answers
|
|
20
|
+
* every unknown path with 200 and the web UI's HTML. So the probe reads the
|
|
21
|
+
* status code *and* the body, and falls back to `/health` only for a 200 that is
|
|
22
|
+
* not JSON — the old image. A 503, or no answer, means "not yet", never "use
|
|
23
|
+
* `/health`": reading an empty answer as "old image" makes a 9.x Gateway count as
|
|
24
|
+
* ready the moment it starts listening.
|
|
25
|
+
*
|
|
26
|
+
* Delete the `/health` fallback once every image is on 9.x.
|
|
27
|
+
*
|
|
28
|
+
* **Async.** A request path must not hold Node's single thread for up to a
|
|
29
|
+
* minute; see docs/ARCHITECTURE.md §3.1.
|
|
30
|
+
*/
|
|
31
|
+
import { dockerExecNoFail } from '@/lib/docker-exec';
|
|
32
|
+
|
|
33
|
+
/** Interval between attempts. The Gateway takes seconds to boot, not milliseconds. */
|
|
34
|
+
const POLL_INTERVAL_MS = 2_000;
|
|
35
|
+
|
|
36
|
+
/** Per-attempt budget. Generous: a loaded host can be slow to answer. */
|
|
37
|
+
const ATTEMPT_TIMEOUT_MS = 5_000;
|
|
38
|
+
|
|
39
|
+
const sleep = (ms: number) => new Promise<void>((r) => setTimeout(r, ms));
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* One HTTP GET from inside the container, with its status code.
|
|
43
|
+
*
|
|
44
|
+
* `curl -w` appends the code as the last line. `status` is 0 when nothing usable
|
|
45
|
+
* came back. A deadline may reject, or resolve with truncated output because
|
|
46
|
+
* `docker exec` can exit 0 on the SIGTERM (docs/ARCHITECTURE.md, "A timeout does
|
|
47
|
+
* not reject"); either way a last line that is not a three-digit code counts as
|
|
48
|
+
* no answer. The image has `curl`, not `wget`.
|
|
49
|
+
*/
|
|
50
|
+
async function probe(container: string, path: string): Promise<{ status: number; body: string }> {
|
|
51
|
+
const out = await dockerExecNoFail(
|
|
52
|
+
container,
|
|
53
|
+
['curl', '-s', '-o', '-', '-w', '\n%{http_code}', `http://127.0.0.1:3000${path}`],
|
|
54
|
+
{ timeoutMs: ATTEMPT_TIMEOUT_MS },
|
|
55
|
+
);
|
|
56
|
+
if (!out) return { status: 0, body: '' };
|
|
57
|
+
const cut = out.lastIndexOf('\n');
|
|
58
|
+
const tail = (cut === -1 ? out : out.slice(cut + 1)).trim();
|
|
59
|
+
if (!/^\d{3}$/.test(tail)) return { status: 0, body: '' };
|
|
60
|
+
return { status: Number(tail), body: cut === -1 ? '' : out.slice(0, cut) };
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function asJson(body: string): Record<string, unknown> | null {
|
|
64
|
+
if (!body.trimStart().startsWith('{')) return null;
|
|
65
|
+
try {
|
|
66
|
+
const v = JSON.parse(body);
|
|
67
|
+
return v && typeof v === 'object' ? (v as Record<string, unknown>) : null;
|
|
68
|
+
} catch {
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Poll a container's Gateway until it has finished starting.
|
|
75
|
+
*
|
|
76
|
+
* Resolves `true` on the first affirmative answer, `false` on timeout. Never
|
|
77
|
+
* throws: an unreachable container is indistinguishable from a slow one until the
|
|
78
|
+
* deadline passes, and every caller treats both the same way.
|
|
79
|
+
*/
|
|
80
|
+
export async function waitForGatewayReady(
|
|
81
|
+
container: string,
|
|
82
|
+
timeoutMs = 60_000,
|
|
83
|
+
): Promise<boolean> {
|
|
84
|
+
const deadline = Date.now() + timeoutMs;
|
|
85
|
+
|
|
86
|
+
while (Date.now() < deadline) {
|
|
87
|
+
const startupz = await probe(container, '/startupz');
|
|
88
|
+
|
|
89
|
+
if (startupz.status === 200) {
|
|
90
|
+
const json = asJson(startupz.body);
|
|
91
|
+
if (json) {
|
|
92
|
+
// 9.x: accept only the answer that means startup finished.
|
|
93
|
+
if (json.status === 'started') return true;
|
|
94
|
+
} else {
|
|
95
|
+
// 200 with a non-JSON body: the 2026.7.1-2 web UI catch-all, so this build
|
|
96
|
+
// has no /startupz. Liveness is the best signal it offers.
|
|
97
|
+
const health = await probe(container, '/health');
|
|
98
|
+
const h = health.status === 200 ? asJson(health.body) : null;
|
|
99
|
+
if (h && (h.ok === true || h.status === 'live')) return true;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
// 503 (starting or draining), any other code, or no answer: not yet.
|
|
103
|
+
|
|
104
|
+
// Only sleep if there is still budget left to use afterwards.
|
|
105
|
+
if (Date.now() + POLL_INTERVAL_MS >= deadline) break;
|
|
106
|
+
await sleep(POLL_INTERVAL_MS);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
return false;
|
|
110
|
+
}
|
package/lib/channelManager.ts
CHANGED
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
* - gateway/config-channels.md
|
|
22
22
|
* - concepts/multi-agent.md (bindings)
|
|
23
23
|
*/
|
|
24
|
-
import { execSync } from 'child_process';
|
|
24
|
+
import { execSync, execFileSync } from 'child_process';
|
|
25
25
|
|
|
26
26
|
// ── Types ────────────────────────────────────
|
|
27
27
|
|
|
@@ -232,7 +232,7 @@ export function removeTelegram(agentId: string): { success: boolean; error?: str
|
|
|
232
232
|
* The agentId in the binding MUST be "main" (OpenClaw's default agent)
|
|
233
233
|
* so that sessions are created as `agent:main:main`.
|
|
234
234
|
* Using the container name as agentId would create sessions like
|
|
235
|
-
* `agent
|
|
235
|
+
* `agent:<AGENT_NAME>:main` which breaks model bindings that target
|
|
236
236
|
* `agent:main:*`.
|
|
237
237
|
*
|
|
238
238
|
* Per multi-agent.md: bindings route (channel, accountId) to agentId.
|
|
@@ -360,33 +360,75 @@ export function approvePairing(agentId: string, code: string, channel: string =
|
|
|
360
360
|
}
|
|
361
361
|
}
|
|
362
362
|
|
|
363
|
-
/**
|
|
363
|
+
/**
|
|
364
|
+
* Script run inside the container to drop one sender from the allowlist file.
|
|
365
|
+
*
|
|
366
|
+
* The sender id arrives as `process.argv[1]`, never interpolated into the source.
|
|
367
|
+
* The previous version built this string by substitution, applying shell-style
|
|
368
|
+
* escaping to a value that lands inside a JavaScript string literal: an id
|
|
369
|
+
* containing an apostrophe closed the literal and the rest ran as code.
|
|
370
|
+
*
|
|
371
|
+
* It prints one of three sentinels so the caller can tell the outcomes apart —
|
|
372
|
+
* `docker exec` exits 0 even when killed by the timeout, so an empty result
|
|
373
|
+
* cannot be read as success (see docs/ARCHITECTURE.md §3.1).
|
|
374
|
+
*/
|
|
375
|
+
const REVOKE_SCRIPT = `
|
|
376
|
+
const fs = require('fs');
|
|
377
|
+
const [p, target] = [process.argv[1], process.argv[2]];
|
|
378
|
+
try {
|
|
379
|
+
if (!fs.existsSync(p)) { console.log('revoke:no-store'); process.exit(0); }
|
|
380
|
+
const d = JSON.parse(fs.readFileSync(p, 'utf8'));
|
|
381
|
+
const before = (d.allowFrom || []).length;
|
|
382
|
+
d.allowFrom = (d.allowFrom || []).filter((e) => String(e) !== target);
|
|
383
|
+
if (d.allowFrom.length === before) { console.log('revoke:absent'); process.exit(0); }
|
|
384
|
+
fs.writeFileSync(p, JSON.stringify(d, null, 2), 'utf8');
|
|
385
|
+
console.log('revoke:ok');
|
|
386
|
+
} catch (e) {
|
|
387
|
+
console.log('revoke:error ' + e.message);
|
|
388
|
+
}
|
|
389
|
+
`;
|
|
390
|
+
|
|
391
|
+
/**
|
|
392
|
+
* Revoke an approved sender from the allowlist.
|
|
393
|
+
*
|
|
394
|
+
* There is no CLI for this. `openclaw pairing` exposes only `approve`, `list` and
|
|
395
|
+
* `help` — verified on both 2026.7.1-2 and 2026.9.3 — so the allowlist store has
|
|
396
|
+
* to be edited directly.
|
|
397
|
+
*
|
|
398
|
+
* The previous implementation tried `openclaw pairing revoke` first and treated
|
|
399
|
+
* any output that lacked "Unknown command" or "not found" as success. OpenClaw
|
|
400
|
+
* actually answers `OpenClaw does not know the command "revoke".`, which contains
|
|
401
|
+
* neither, so every revocation reported success, changed nothing, and never
|
|
402
|
+
* reached the fallback.
|
|
403
|
+
*
|
|
404
|
+
* On 2026.9.x this store moved into SQLite and the file is gone. Rather than
|
|
405
|
+
* silently doing nothing again, that case now fails loudly. Revocation against 9.x's SQLite store is not
|
|
406
|
+
* implemented yet.
|
|
407
|
+
*/
|
|
364
408
|
export function revokePairing(agentId: string, senderId: string, channel: string = 'telegram'): { success: boolean; error?: string } {
|
|
365
409
|
const container = findContainerById(agentId);
|
|
366
410
|
if (!container) return { success: false, error: `Agent container not found: ${agentId}` };
|
|
367
411
|
|
|
368
412
|
try {
|
|
369
|
-
//
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
return { success: true };
|
|
377
|
-
}
|
|
413
|
+
// execFileSync, not a shell: argv is passed through untouched, so nothing in
|
|
414
|
+
// `senderId` can be read as syntax.
|
|
415
|
+
const out = execFileSync(
|
|
416
|
+
'docker',
|
|
417
|
+
['exec', container, 'node', '-e', REVOKE_SCRIPT, pairingAllowFile(channel), senderId],
|
|
418
|
+
{ encoding: 'utf-8', timeout: 15000, maxBuffer: 1024 * 1024 },
|
|
419
|
+
).trim();
|
|
378
420
|
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
421
|
+
if (out.includes('revoke:ok')) return { success: true };
|
|
422
|
+
if (out.includes('revoke:absent')) return { success: true }; // already gone — the desired state
|
|
423
|
+
if (out.includes('revoke:no-store')) {
|
|
424
|
+
return {
|
|
425
|
+
success: false,
|
|
426
|
+
error: 'No allowlist file in this container: nobody has been approved on this '
|
|
427
|
+
+ 'channel yet, or the agent runs OpenClaw 9.x, whose pairing store is in '
|
|
428
|
+
+ 'SQLite and cannot be revoked from here yet.',
|
|
429
|
+
};
|
|
387
430
|
}
|
|
388
|
-
|
|
389
|
-
return { success: true };
|
|
431
|
+
return { success: false, error: out || 'revoke produced no output' };
|
|
390
432
|
} catch (e) {
|
|
391
433
|
return { success: false, error: (e as Error).message };
|
|
392
434
|
}
|