@flame0510/project-aether 1.3.0 → 1.4.1

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.
Files changed (78) hide show
  1. package/README.md +1 -0
  2. package/agent-templates/README.md +42 -22
  3. package/agent-templates/base-image/Dockerfile +42 -33
  4. package/agent-templates/base-image/entrypoint.sh +67 -12
  5. package/app/agents/BrowserAccessSection.tsx +510 -0
  6. package/app/agents/ChannelManager.tsx +19 -11
  7. package/app/agents/ImageDownloadBanner.tsx +53 -19
  8. package/app/agents/ModelSection.tsx +4 -1
  9. package/app/agents/PageClient.tsx +629 -167
  10. package/app/agents/UpdateSection.tsx +300 -0
  11. package/app/agents/create/PageClient.tsx +11 -49
  12. package/app/agents/create/page.tsx +14 -28
  13. package/app/api/agents/[id]/backup/route.ts +26 -69
  14. package/app/api/agents/[id]/channels/pairing/route.ts +3 -3
  15. package/app/api/agents/[id]/channels/telegram/route.ts +2 -2
  16. package/app/api/agents/[id]/cold-backup/route.ts +56 -0
  17. package/app/api/agents/[id]/devices/route.ts +126 -0
  18. package/app/api/agents/[id]/invite-link/route.ts +53 -0
  19. package/app/api/agents/[id]/lifecycle/route.ts +3 -0
  20. package/app/api/agents/[id]/open-control-ui/route.ts +58 -0
  21. package/app/api/agents/[id]/recreate/route.ts +33 -163
  22. package/app/api/agents/[id]/restart/route.ts +5 -0
  23. package/app/api/agents/[id]/restore/route.ts +40 -70
  24. package/app/api/agents/[id]/route.ts +38 -150
  25. package/app/api/agents/[id]/update/rollback/route.ts +30 -0
  26. package/app/api/agents/[id]/update/route.ts +50 -0
  27. package/app/api/agents/activity-summary/route.ts +67 -0
  28. package/app/api/agents/create/route.ts +38 -92
  29. package/app/api/agents/devices-summary/route.ts +37 -0
  30. package/app/api/agents/download-image/route.ts +16 -9
  31. package/app/api/agents/image-status/route.ts +31 -111
  32. package/app/api/agents/route.ts +25 -49
  33. package/app/api/agents/token/route.ts +33 -10
  34. package/app/api/assistant/route.ts +2 -2
  35. package/app/api/gateway/agent/route.ts +14 -0
  36. package/app/api/gateway/provider/balance/route.ts +5 -2
  37. package/app/api/gateway/sync.ts +97 -14
  38. package/app/api/setup/agent-image/route.ts +14 -42
  39. package/app/api/version/route.ts +2 -1
  40. package/app/components/DashboardToolbar.tsx +1 -1
  41. package/app/gateway/PageClient.tsx +27 -32
  42. package/bin/rev4a.js +43 -41
  43. package/daemon.js +6 -6
  44. package/docs/ARCHITECTURE.md +107 -9
  45. package/docs/FRONTEND-ARCHITECTURE.md +8 -1
  46. package/docs/REV4A.md +54 -17
  47. package/docs/dev/API-REFERENCE.md +573 -105
  48. package/docs/dev/DATABASE.md +96 -0
  49. package/docs/dev/GATEWAY.md +21 -6
  50. package/docs/rag/DATA-FRESHNESS.md +6 -4
  51. package/docs/rag/GLOSSARY.md +12 -3
  52. package/docs/rag/REV4A-OVERVIEW.md +18 -5
  53. package/docs/rag/WHAT-I-CAN-ANSWER.md +6 -2
  54. package/instrumentation.ts +43 -0
  55. package/lib/agent-busy.ts +21 -0
  56. package/lib/agent-devices.ts +361 -0
  57. package/lib/agent-edit-state.ts +108 -0
  58. package/lib/agent-edit.ts +149 -0
  59. package/lib/agent-images.ts +375 -0
  60. package/lib/agent-ports-server.ts +78 -0
  61. package/lib/agent-ports.ts +91 -0
  62. package/lib/agent-recreate-state.ts +108 -0
  63. package/lib/agent-recreate.ts +359 -0
  64. package/lib/agent-restore-state.ts +107 -0
  65. package/lib/agent-restore.ts +141 -0
  66. package/lib/agent-setup.ts +66 -17
  67. package/lib/agent-update-state.ts +122 -0
  68. package/lib/agent-update.ts +456 -0
  69. package/lib/agent-versions.json +14 -0
  70. package/lib/agent-versions.ts +80 -0
  71. package/lib/buildAgentImage.ts +88 -290
  72. package/lib/channelManager.ts +149 -102
  73. package/lib/cold-backup.ts +354 -0
  74. package/lib/credentials/delivery.ts +3 -3
  75. package/lib/db-bootstrap.mjs +76 -0
  76. package/lib/docker-utils.ts +3 -3
  77. package/lib/provider-balance.ts +33 -12
  78. package/package.json +1 -1
@@ -107,6 +107,102 @@ CREATE INDEX idx_metrics_ts ON system_metrics(ts);
107
107
 
108
108
  Rows older than 24 h are pruned automatically by the daemon.
109
109
 
110
+ ### `agent_upgrades` — agent updates and rollbacks
111
+
112
+ ```sql
113
+ CREATE TABLE agent_upgrades (
114
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
115
+ agent_id TEXT NOT NULL,
116
+ status TEXT NOT NULL, -- pending, backing_up, migrating, verifying, done, failed,
117
+ -- interrupted, rolling_back, rolled_back, rollback_failed
118
+ from_version TEXT,
119
+ to_version TEXT NOT NULL,
120
+ backup_file TEXT, -- pre-update cold backup in the rev4a-backups volume
121
+ baseline_json TEXT, -- transcript events per session and cron job names before
122
+ verify_json TEXT, -- the comparison after the update
123
+ error TEXT,
124
+ started_at INTEGER NOT NULL,
125
+ updated_at INTEGER NOT NULL,
126
+ finished_at INTEGER
127
+ );
128
+
129
+ CREATE INDEX idx_agent_upgrades_agent ON agent_upgrades(agent_id, started_at);
130
+ ```
131
+
132
+ One row per update attempt (`lib/agent-update.ts`); a rollback moves the same row on to
133
+ `rolling_back`. Created by `lib/db-bootstrap.mjs`. At startup, rows still in an active
134
+ status are marked `interrupted`. Rows are never pruned.
135
+
136
+ ### `agent_recreates` — agent recreates
137
+
138
+ ```sql
139
+ CREATE TABLE agent_recreates (
140
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
141
+ agent_id TEXT NOT NULL,
142
+ status TEXT NOT NULL, -- backing_up, recreating, done, failed, interrupted
143
+ image TEXT, -- the image the container is rebuilt on
144
+ backup_file TEXT, -- pre-recreate cold backup in the rev4a-backups volume
145
+ error TEXT,
146
+ started_at INTEGER NOT NULL,
147
+ updated_at INTEGER NOT NULL,
148
+ finished_at INTEGER
149
+ );
150
+
151
+ CREATE INDEX idx_agent_recreates_agent ON agent_recreates(agent_id, started_at);
152
+ ```
153
+
154
+ One row per recreate attempt (`lib/agent-recreate.ts`), the same-version counterpart of
155
+ an update: a cold backup, the container rebuilt, the gateway waited for. It is written
156
+ so a page reload or a Rev4a restart mid-way is visible instead of lost
157
+ (`GET /api/agents/[id]/recreate`). At startup, rows still in an active status become
158
+ `interrupted` and the agent is started again once its backup helper has finished. Rows
159
+ are never pruned.
160
+
161
+ ### `agent_restores` — agent restores
162
+
163
+ ```sql
164
+ CREATE TABLE agent_restores (
165
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
166
+ agent_id TEXT NOT NULL,
167
+ status TEXT NOT NULL, -- restoring, done, failed, interrupted
168
+ file TEXT, -- the archive being applied, in the rev4a-backups volume
169
+ error TEXT,
170
+ started_at INTEGER NOT NULL,
171
+ updated_at INTEGER NOT NULL,
172
+ finished_at INTEGER
173
+ );
174
+
175
+ CREATE INDEX idx_agent_restores_agent ON agent_restores(agent_id, started_at);
176
+ ```
177
+
178
+ One row per restore attempt (`lib/agent-restore.ts`): the volume is replaced from the
179
+ archive. Persisted for the same reason as a recreate (`GET /api/agents/[id]/restore`),
180
+ and at startup an active row becomes `interrupted` with the container started again.
181
+ Rows are never pruned.
182
+
183
+ ### `agent_edits` — agent renames and port changes
184
+
185
+ ```sql
186
+ CREATE TABLE agent_edits (
187
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
188
+ agent_id TEXT NOT NULL,
189
+ status TEXT NOT NULL, -- rebuilding, done, failed, interrupted
190
+ display_name TEXT, -- the name being applied, when it was a rename
191
+ port_range TEXT, -- the range being applied, when it was a port change
192
+ error TEXT,
193
+ started_at INTEGER NOT NULL,
194
+ updated_at INTEGER NOT NULL,
195
+ finished_at INTEGER
196
+ );
197
+
198
+ CREATE INDEX idx_agent_edits_agent ON agent_edits(agent_id, started_at);
199
+ ```
200
+
201
+ One row per edit attempt (`lib/agent-edit.ts`): the container is rebuilt with the new
202
+ name/ports, no backup involved. Persisted for the same reason as the others
203
+ (`GET /api/agents/[id]`); at startup an active row becomes `interrupted` with the
204
+ container started again. Rows are never pruned.
205
+
110
206
  ## Useful Queries
111
207
 
112
208
  ```sql
@@ -1,6 +1,6 @@
1
1
  # Gateway Page
2
2
 
3
- > **Last updated:** 2026-09-14
3
+ > **Last updated:** 2026-09-15
4
4
 
5
5
  The Gateway page (`/gateway`) is the control panel for provider configuration:
6
6
  API keys and the model catalogue offered to every agent container. It does not
@@ -170,7 +170,10 @@ Saving calls `PUT /api/gateway/agent`, which writes the container's
170
170
  container's existing list untouched, `[]` clears it. The Model panel is the only
171
171
  UI that sets them; the create wizard does not
172
172
  - **no restart** — `agents` is in OpenClaw's hot-reload column, so the write
173
- applies on the agent's next turn
173
+ applies on the agent's next turn. Exception on OpenClaw 2026.7.1-2: a Telegram
174
+ channel keeps the configuration it had when it started, so Telegram replies stay on
175
+ the old model until the container is restarted. The Control UI picks the change up
176
+ at once. OpenClaw 9.x reads the configuration per message
174
177
 
175
178
  The agent list shows each agent's current model as a chip, fed by
176
179
  `GET /api/agents/models-summary`. The red states are split by remedy, because they
@@ -218,6 +221,9 @@ Two failures, handled differently on purpose:
218
221
 
219
222
  - **Docker unreachable** — refused before anything is written: `503`, nothing saved,
220
223
  the checkbox does not move, so the checkboxes never disagree with every agent.
224
+ - **An agent under a cold backup** is skipped: writing its config while tar reads the
225
+ volume would put a half-changed file in the archive. It is listed in `sync.failed`
226
+ with the reason; the next sync catches it up.
221
227
  - **Some containers fail once Docker answers** — the toggle stands and `sync.failed`
222
228
  lists them; the page shows it in amber, and an agent whose primary is affected shows
223
229
  `OUT OF SYNC`. Rolling
@@ -359,18 +365,27 @@ Restart the Rev4a server (via systemd).
359
365
  4. **No gateway restart after model sync** — The sync module writes `models.providers`
360
366
  to the file directly without restarting the gateway.
361
367
 
368
+ The model `input` list follows each agent's OpenClaw version
369
+ (`lib/agent-versions.json`, resolved by `containerVersionSync()`): the newest release
370
+ accepts `text | image | audio | video`, while `2026.7.1-2` accepts only `text | image`,
371
+ and one unknown value makes it discard the whole generated catalogue
372
+ (`model catalog load issue`). That is why the previous version stays in the table
373
+ while agents still run it.
374
+
362
375
  5. **Entrypoint does not generate openclaw.json** — The entrypoint resolves the
363
376
  auth token and starts the gateway with `--allow-unconfigured`; OpenClaw
364
377
  generates its own default config on first boot. All post-creation config
365
378
  (controlUi, model refs, providers) is written by the create route
366
379
  (`POST /api/agents/create`) after the gateway is fully ready.
367
- The entrypoint also runs `openclaw doctor --non-interactive` **before** the
368
- gateway, but only when the OpenClaw version changed since last boot
369
- (tracked in `/root/.openclaw/.last-version`) — safe migrations only, no restart.
380
+ Before the gateway starts, the entrypoint runs `openclaw doctor --fix --non-interactive`
381
+ when the OpenClaw version changed since the last successful run (tracked in
382
+ `/root/.openclaw/.last-version`), and fills in the image defaults that are missing:
383
+ plugin load path, web search provider, heartbeat off. See
384
+ [REV4A.md](../REV4A.md#agent-templates).
370
385
 
371
386
  6. **Model ref written after gateway readiness** — When the wizard creates an agent,
372
387
  the create route waits for the gateway to finish starting (`waitForGatewayReady`), then writes
373
- `agents.defaults.model.primary` + fallbacks and `gateway.controlUi.allowedOrigins`,
388
+ `agents.defaults.model.primary` + fallbacks and the Control UI origin policy (`gateway.controlUi`),
374
389
  then writes the provider block from `buildRev4aProviderConfig()` with `openclaw config patch --stdin`. The Gateway sync (`PUT /api/gateway/provider`)
375
390
  never touches model references — only `models.providers` is synced.
376
391
 
@@ -1,6 +1,6 @@
1
1
  # Data Freshness in Rev4a
2
2
 
3
- > **Last updated:** 2026-09-13
3
+ > **Last updated:** 2026-09-15
4
4
 
5
5
  Understanding how current the data in Rev4a is — and what is truly real-time vs. periodically updated.
6
6
 
@@ -90,9 +90,11 @@ Agents page.
90
90
 
91
91
  **Update frequency:** polled every 2 seconds, continuously
92
92
 
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.
93
+ `GET /api/agents/image-status` lists the supported OpenClaw versions downloaded and
94
+ compares them with the versions the registry publishes. The registry list is refreshed
95
+ at most every 10 minutes, so a newly published version can take that long to appear.
96
+ The banner polls the endpoint every 2 seconds for as long as the page is open, not only
97
+ while something is running.
96
98
 
97
99
  The operation the banner triggers is a **`docker pull`**, not a build. While it
98
100
  runs the banner shows an indeterminate animated bar: there is no percentage, no
@@ -1,6 +1,6 @@
1
1
  # Rev4a Glossary
2
2
 
3
- > **Last updated:** 2026-09-14
3
+ > **Last updated:** 2026-09-15
4
4
 
5
5
  Terms you'll encounter while using the Rev4a dashboard.
6
6
 
@@ -18,6 +18,9 @@ A small inline label component used for status indicators and channel chips. Six
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
+ ## Browser Access
22
+ Which browsers may open an agent's Control UI. On OpenClaw 9.x every new browser must be approved once; the approval is remembered per browser. Managed in the "BROWSER ACCESS" section of the agent detail panel: approve or reject waiting browsers, rename or revoke approved ones. The Open button uses a one-time link that skips the approval. The Invite link button gives a link for someone else: their browser still waits for approval.
23
+
21
24
  ## Container
22
25
  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
26
 
@@ -73,11 +76,14 @@ Rev4a's built-in proxy that gives all agents unified access to configured LLM pr
73
76
  The AI concierge embedded in Rev4a. Click the floating chat button (bottom-right) on any page to ask questions about the dashboard features and navigation.
74
77
 
75
78
  ## Recreate (Agent)
76
- Rebuild an agent container from the latest `openclaw-agent-base:latest` image while preserving the persistent volume. Auto-backups the volume first — if the backup fails, the recreate is aborted. This is how agents pick up OpenClaw image updates.
79
+ Rebuild an agent container on its own OpenClaw version while preserving the persistent volume. Auto-backups the volume first — if the backup fails, the recreate is aborted. A recreate never changes the OpenClaw version; moving to a newer version is a separate update.
77
80
 
78
81
  ## Restore (Agent)
79
82
  Replace an agent's persistent volume with a previously created backup. The container is stopped, the volume content is fully replaced, then the container is restarted.
80
83
 
84
+ ## Rollback (Agent)
85
+ Undo an agent's latest OpenClaw update: the backup taken just before the update is put back and the agent starts again on its previous version. Anything the agent did after the update is lost. Offered in the agent's OPENCLAW VERSION section while that backup exists.
86
+
81
87
  ## Session
82
88
  One instance of an agent doing work. Tracks: start/end time, tokens used, cost, model, status, and task description. A session starts when an agent receives a task and ends when it completes or fails.
83
89
 
@@ -113,7 +119,10 @@ A capability available to agents: running shell commands, reading files, searchi
113
119
  A record of an agent using a tool during a session. Useful for auditing what an agent actually did. Shown in the Session Drawer.
114
120
 
115
121
  ## Traefik
116
- Optional reverse proxy for routing web traffic to agent containers. If configured, an agent gets a public URL. Rev4a itself runs on port 3740 and does not require Traefik.
122
+ Optional reverse proxy an operator can put in front of the Rev4a dashboard or of an agent's port. Rev4a does not configure it and sets no routing labels on agents: an agent's Control UI is reached on its published port. Rev4a itself runs on port 3740 and does not require Traefik.
123
+
124
+ ## Update (Agent)
125
+ Move one agent to a newer OpenClaw version downloaded on the Agents page, from its OPENCLAW VERSION section. The agent goes offline for a few minutes: it is backed up while stopped, started on the new version (which converts its data), and checked for missing conversation history or cron jobs. Undone with Rollback.
117
126
 
118
127
  ## Vault
119
128
  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.
@@ -1,6 +1,6 @@
1
1
  # What is Rev4a?
2
2
 
3
- > **Last updated:** 2026-09-13
3
+ > **Last updated:** 2026-09-15
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
 
@@ -34,10 +34,23 @@ Manage Docker containers running OpenClaw agents. See which agents are running,
34
34
 
35
35
  The agent list also shows a compact TG chip next to each agent name (green = connected, hidden if not configured).
36
36
 
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**.
37
+ **Browser access:** on agents running OpenClaw 9.x, every new browser has to be approved once before the agent's Control UI connects. The detail panel's "BROWSER ACCESS" section lists browsers waiting for approval, with Approve and Reject, and the browsers already approved, with Rename and Revoke. A yellow "BROWSER WAITING" chip on the agent card says a request is pending. The **Open** button tries a one-time link that lets the browser in without any approval; if the agent cannot issue one (agents still on OpenClaw 2026.7.x) it opens the normal link, which on those agents needs no approval anyway. To let someone else in, **Invite link** gives a link to send them: their browser appears under waiting for approval, and nothing opens until you approve it. The link contains the token all agents share; changing the agents token cancels every link already sent.
38
+
39
+ The Agents page includes a banner for the **agent base image**, which is kept per
40
+ OpenClaw version. It is hidden when a supported version is downloaded and nothing newer
41
+ is published. When no image is downloaded it offers **Download Image**; when a newer
42
+ supported OpenClaw version is published it says so and offers **Download <version>**.
43
+ Downloading changes no agent: new agents are created on the newest version downloaded,
44
+ existing agents keep their version (a recreate never changes it), and their card shows
45
+ **UPDATE AVAILABLE**.
46
+
47
+ **Updating an agent:** the detail panel's "OPENCLAW VERSION" section shows the version the
48
+ agent runs and, when a newer one is downloaded, **Update to <version>**. The update takes
49
+ the agent offline for a few minutes: it backs the agent up while stopped, starts it on the
50
+ new version (which converts its data), and checks that no conversation history or cron job
51
+ went missing. Afterwards **Roll back to <version>** puts back the backup taken just before
52
+ the update, on the old version; anything the agent did after the update is lost. The agent
53
+ must be running to start an update.
41
54
 
42
55
  That button runs a `docker pull` from the registry. It is a download, not a build.
43
56
  While it runs the banner shows an indeterminate animated bar with no percentage and
@@ -1,6 +1,6 @@
1
1
  # What PULSE Can Answer
2
2
 
3
- > **Last updated:** 2026-09-14
3
+ > **Last updated:** 2026-09-15
4
4
 
5
5
  PULSE is the in-dashboard AI concierge for Rev4a. This document defines what she can and cannot answer.
6
6
 
@@ -48,13 +48,17 @@ PULSE is the in-dashboard AI concierge for Rev4a. This document defines what she
48
48
  - "How do I sync credentials to an agent?"
49
49
  - "How do I back up an agent?"
50
50
  - "How do I restore an agent from a backup?"
51
- - "How do I recreate an agent with the latest image?"
51
+ - "How do I rebuild (recreate) an agent's container?"
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
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
+ - "Why does the Control UI ask me to approve this browser?"
59
+ - "How do I approve or revoke a browser for an agent?"
60
+ - "How do I give someone else access to an agent's Control UI?"
61
+ - "How do I update an agent to a newer OpenClaw version, and how do I go back?"
58
62
  - "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
63
  - "How do I manage agent model fallbacks?"
60
64
  - "How do I connect an agent to Telegram?"
@@ -22,5 +22,48 @@ export async function register() {
22
22
  // Best-effort — don't block startup if sync fails
23
23
  console.warn('[rev4a] Failed to sync agents on startup');
24
24
  }
25
+ // Updates cut off by the restart: no job runs in a fresh process, so any update
26
+ // still marked active becomes `interrupted` and waits for the operator.
27
+ try {
28
+ const { recoverInterruptedUpdates } = await import('./lib/agent-update');
29
+ const interrupted = await recoverInterruptedUpdates();
30
+ if (interrupted) console.warn(`[rev4a] ${interrupted} agent update(s) were interrupted by the restart`);
31
+ } catch {
32
+ console.warn('[rev4a] Could not check agent updates on startup');
33
+ }
34
+ // Recreates cut off by the restart: the row becomes `interrupted` and an agent the
35
+ // backup had stopped is started again, so nothing stays down.
36
+ try {
37
+ const { recoverInterruptedRecreates } = await import('./lib/agent-recreate');
38
+ const interrupted = await recoverInterruptedRecreates();
39
+ if (interrupted) console.warn(`[rev4a] ${interrupted} agent recreate(s) were interrupted by the restart`);
40
+ } catch {
41
+ console.warn('[rev4a] Could not check agent recreates on startup');
42
+ }
43
+ // Restores cut off by the restart: same treatment — the row becomes `interrupted`
44
+ // and a container left stopped is started again.
45
+ try {
46
+ const { recoverInterruptedRestores } = await import('./lib/agent-restore');
47
+ const interrupted = await recoverInterruptedRestores();
48
+ if (interrupted) console.warn(`[rev4a] ${interrupted} agent restore(s) were interrupted by the restart`);
49
+ } catch {
50
+ console.warn('[rev4a] Could not check agent restores on startup');
51
+ }
52
+ // Edits cut off by the restart: same treatment.
53
+ try {
54
+ const { recoverInterruptedEdits } = await import('./lib/agent-edit');
55
+ const interrupted = await recoverInterruptedEdits();
56
+ if (interrupted) console.warn(`[rev4a] ${interrupted} agent edit(s) were interrupted by the restart`);
57
+ } catch {
58
+ console.warn('[rev4a] Could not check agent edits on startup');
59
+ }
60
+ // Cold backups whose helper outlived a Rev4a restart: finish the exited ones (the
61
+ // agent is started again if it was running) and keep watching the rest.
62
+ try {
63
+ const { reconcileColdBackups } = await import('./lib/cold-backup');
64
+ await reconcileColdBackups();
65
+ } catch {
66
+ console.warn('[rev4a] Could not reconcile cold backups on startup');
67
+ }
25
68
  }
26
69
  }
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Whether an agent is in the middle of a long operation that nothing else may touch:
3
+ * an edit (lib/agent-edit.ts), a restore (lib/agent-restore.ts), a recreate
4
+ * (lib/agent-recreate.ts), an update (lib/agent-update.ts) or a cold backup
5
+ * (lib/cold-backup.ts). The routes that change an agent answer 409 with this reason.
6
+ */
7
+ import { isColdBackupRunning } from '@/lib/cold-backup';
8
+ import { isUpdateActive } from '@/lib/agent-update-state';
9
+ import { isRecreateActive } from '@/lib/agent-recreate-state';
10
+ import { isRestoreActive } from '@/lib/agent-restore-state';
11
+ import { isEditActive } from '@/lib/agent-edit-state';
12
+
13
+ export async function agentBusyReason(agentId: string): Promise<string | null> {
14
+ if (isUpdateActive(agentId)) return 'An update of this agent is running';
15
+ if (isRecreateActive(agentId)) return 'A recreate of this agent is running';
16
+ if (isRestoreActive(agentId)) return 'A restore of this agent is running';
17
+ if (isEditActive(agentId)) return 'An edit of this agent is running';
18
+ if (await isColdBackupRunning(agentId)) return 'A backup of this agent is running';
19
+ return null;
20
+ }
21
+