ai-agentmanager 1.0.0 → 1.0.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.
package/CHANGELOG.md ADDED
@@ -0,0 +1,48 @@
1
+ # Changelog
2
+
3
+ ## 1.0.1 (2026-10-26)
4
+
5
+ - **Name:** the product is now called **AI Agent Manager**, matching the `ai-agentmanager` package. The command is still `agentmgr`.
6
+ - **About:** the version next to the title opens an About box with the release date, author, license, links and this daemon's details; **Check for updates** asks npm for the newest version. `agentmgr about` (`--check`) in a terminal.
7
+ - **Dependencies:** DOMPurify 3.4.17, body-parser 2.4.0, Mermaid 12.
8
+
9
+ ## 1.0.0 (2026-10-25)
10
+
11
+ - **License:** Apache-2.0 (was MIT).
12
+ - **Remote access is off until you turn it on** (`agentmgr remote on`). Installs that had it on keep it on.
13
+ - **AI judge visibility:** the bottom of the Inbox shows today's calls with **Turn off** / **Turn on**; also `agentmgr judge [on|off]`.
14
+ - **Hub:** `agentmgr hub serve` also listens on this Mac's Tailscale address, and **Add machine** gives a join command other machines can reach (or says why they can't).
15
+ - **Linux:** a clear message when Chrome's sandbox can't start; `browserNoSandbox` to run without it.
16
+ - **Upgrades:** the state file is versioned, backed up at startup, migrated with a backup, and never overwritten by an older version.
17
+ - **Plugins:** policies that can refuse actions, audit listeners, extra API routes and engines on the daemon; sign-in methods, request policies and audit on the hub ([docs/plugins.md](docs/plugins.md)).
18
+ - **Packaging:** `npm i -g ai-agentmanager` (the command is `agentmgr`), `agentmgr --version`.
19
+ - **Installer:** the site's `install.sh` puts the program in `~/.local/share/agentmanager` and never touches `~/.agentmanager`, where your agents and workspaces live. It used to replace that folder.
20
+ - **Start at login:** `agentmgr service install` keeps `AGENTMGR_PLUGINS` and `AGENTMGR_HUB_PLUGINS`.
21
+ - **UI:** long agent names stay on one line, with the buttons below.
22
+ - **Project:** CI on macOS and Linux, SECURITY.md, CONTRIBUTING.md, code of conduct, architecture notes.
23
+
24
+ ## 0.5.0 (2026-10-23)
25
+
26
+ - **AI judge:** rules decide when to look, a fast model decides what is going on: stuck agents (with a specific nudge), idle screens no rule matched, whether "done" work meets the intent, colliding changes, backlog order and splits, and which alerts interrupt you. Learns from your reactions.
27
+ - **CLI sign-in links:** `railway login`, `gh auth login --web` and similar open where the callback reaches the waiting CLI, from any device.
28
+ - **Live browser tabs:** a real Chrome on the machine, streamed into the tab, with typing, scrolling, popups as tabs, the phone keyboard, sharp frames on retina screens and one-way sound.
29
+ - **Continue terminal sessions:** import Claude Code sessions started with a plain `claude` and continue them as agents.
30
+
31
+ ## 0.4.0 (2026-10-21)
32
+
33
+ - **Engines:** Codex CLI and Antigravity CLI next to Claude Code, behind one adapter.
34
+ - **Browser tabs:** open any address next to an agent's terminals, relayed over HTTPS to signed-in remote devices.
35
+ - **Human vs AI** review with a second-model critique; optional plan review before an agent implements.
36
+ - **Hub sign-in links:** open a machine's own page signed in, with a one-time ticket.
37
+
38
+ ## 0.3.0 (2026-10-18)
39
+
40
+ - **Hub:** one place for agents on several machines: fleet view, control, a shared backlog that starts work where there's room, Google sign-in.
41
+
42
+ ## 0.2.0 (2026-10-18)
43
+
44
+ - **Decision inbox, conflict radar, stuck detection, checkpoints and forks, races.**
45
+
46
+ ## 0.1.x (2026-10-03 to 2026-10-09)
47
+
48
+ - First release: one background agent per intent, CLI, daemon and web UI, worktrees per agent, briefs, results, phone access, multiple shells and file attachments, swipe to scroll on phones.
package/NOTICE CHANGED
@@ -1,4 +1,4 @@
1
- Agent Manager
1
+ AI Agent Manager
2
2
  Copyright 2026 Jagadesh Munta
3
3
 
4
4
  Licensed under the Apache License, Version 2.0 (see LICENSE).
package/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # Agent Manager
1
+ # AI Agent Manager
2
2
 
3
3
  Run one background [Claude Code](https://claude.com/claude-code) agent per intent, and keep track of them from a web UI or the `agentmgr` CLI.
4
4
 
@@ -39,8 +39,9 @@ Run one background [Claude Code](https://claude.com/claude-code) agent per inten
39
39
  - **Shells next to each agent.** **Open shell** gives a terminal in the agent's folder for running commands by hand. **+ Shell** opens as many more as you need, each in its own tab.
40
40
  - **Browser tabs.** **+ Browser** opens a page as this machine sees it, in a tab next to the agent's terminals. That can be the agent's dev server on `localhost:5173`, a site on its network or VPN, or any address. Local addresses the agent prints are offered as suggestions. It works from your phone or laptop too (see below).
41
41
  - **Live browser.** Browser tabs run a real Chrome on this machine and stream it into the tab: you see its screen and your clicks, scrolling, typing and paste go back to it, from the laptop or the phone. Because the page runs here, `localhost` is this machine, sites that refuse to be framed load normally, and popups (a sign-in window) open as tabs of their own and come back when they close. Chrome (or Chromium, Brave, Edge; `browserBin` in config.json picks one) runs headless with its own profile in `~/.agentmanager/browser-profile`, so sign-ins are remembered; it starts on the first view and stops after 15 minutes with nobody watching. Downloads go to `~/Downloads/agentmanager`. A site that still refuses it as a bot may accept `"browserHeadless": false`, which runs Chrome with a real window on this machine's screen. On Linux where Chrome's sandbox can't start (containers, Ubuntu 23.10+ user-namespace restrictions) the tab says so; `"browserNoSandbox": true` runs it without the sandbox, at your own risk. **Lite** on the bar switches a tab to the frame mode (the page loads in your own browser; quicker for a dev server).
42
- - **CLI sign-in links.** When a terminal prints a sign-in link that returns to a port on this machine (`railway login`, `gh auth login --web`, `gcloud auth login`...), a bar above the terminal offers **Open sign-in**: the link opens in a live browser tab here, so the provider's redirect to `http://127.0.0.1:PORT/callback` reaches the waiting CLI by itself, from any device. It opens on its own if you just typed the command. Without a live browser, it opens a window on your device; from another device you then paste the address you land on (`127.0.0.1:PORT/callback…`) into the bar and Agent Manager delivers it to the CLI, only on the port the link named.
42
+ - **CLI sign-in links.** When a terminal prints a sign-in link that returns to a port on this machine (`railway login`, `gh auth login --web`, `gcloud auth login`...), a bar above the terminal offers **Open sign-in**: the link opens in a live browser tab here, so the provider's redirect to `http://127.0.0.1:PORT/callback` reaches the waiting CLI by itself, from any device. It opens on its own if you just typed the command. Without a live browser, it opens a window on your device; from another device you then paste the address you land on (`127.0.0.1:PORT/callback…`) into the bar and AI Agent Manager delivers it to the CLI, only on the port the link named.
43
43
  - **Attach files and screenshots.** Drop files on the terminal, paste a screenshot, or use **📎 Attach**. The file is saved on the daemon's machine and its path is pasted into the terminal, which is how Claude Code picks up images and files.
44
+ - **About.** The version next to the title opens the About box: release date, author, license, links, and how this daemon runs (data folder, port, Node.js, which `claude`). **Check for updates** asks npm for the newest version. In a terminal: `agentmgr about` (`--check` for updates).
44
45
  - **General sessions.** **Ask Claude** starts a session with no repo, folder or intent, for questions. It is tracked and resumable like any other agent.
45
46
 
46
47
  New in 0.2, for running many agents at once:
@@ -60,13 +61,13 @@ Runs on macOS and Linux; on Windows, use WSL2. Requires Node 20+, `git`, and the
60
61
 
61
62
  ```sh
62
63
  npm install -g ai-agentmanager # the latest release
63
- npm install -g ai-agentmanager@1.0.0 # or a specific one
64
+ npm install -g ai-agentmanager@1.0.1 # or a specific one
64
65
  ```
65
66
 
66
67
  Or from the repository, at a release tag:
67
68
 
68
69
  ```sh
69
- git clone --branch v1.0.0 --depth 1 https://github.com/jdmunta/ai-agentmanager-core.git
70
+ git clone --branch v1.0.1 --depth 1 https://github.com/jdmunta/ai-agentmanager-core.git
70
71
  cd ai-agentmanager-core
71
72
  npm install
72
73
  npm link # optional: puts `agentmgr` on your PATH
@@ -207,7 +208,7 @@ Every agent gets **its own worktree and its own branch** (`agent/<name>-<id>`) o
207
208
 
208
209
  ## Continue sessions started in a terminal
209
210
 
210
- **Import** (next to **Save now** in the agent list) lists the Claude Code sessions you started yourself with `claude` in a terminal on this machine, from the last 7, 30 or 90 days: grouped by folder, with title, first and last ask, branch, size and last activity. Headless runs (`claude -p`, the SDK) and Agent Manager's own agents are left out. Each one you pick becomes an agent in the same folder that continues the same conversation (`--resume`), with the brief, inbox, PRs and usage tracked from then on.
211
+ **Import** (next to **Save now** in the agent list) lists the Claude Code sessions you started yourself with `claude` in a terminal on this machine, from the last 7, 30 or 90 days: grouped by folder, with title, first and last ask, branch, size and last activity. Headless runs (`claude -p`, the SDK) and AI Agent Manager's own agents are left out. Each one you pick becomes an agent in the same folder that continues the same conversation (`--resume`), with the brief, inbox, PRs and usage tracked from then on.
211
212
 
212
213
  A session still open in a terminal is marked **open in a terminal** (a running `claude` names it) or **maybe open** (a `claude` runs in that folder and the session was active minutes ago). Two places writing one conversation would mix it up, so those continue as a **copy** under a new session ID and the terminal keeps the original. For the same reason, an agent that follows a `/clear` to a new session never picks up a headless run or a session a terminal has open in its folder.
213
214
 
@@ -356,7 +357,7 @@ The **Results** card is the first thing in the Brief panel, and `agentmgr show`
356
357
  - **Blockers first, in red.**
357
358
  - **What was delivered.**
358
359
 
359
- It combines what the agent wrote under `## Results` with what Agent Manager tracks itself:
360
+ It combines what the agent wrote under `## Results` with what AI Agent Manager tracks itself:
360
361
 
361
362
  - PRs (with their state).
362
363
  - Failing checks.
@@ -463,7 +464,7 @@ The data set is small.
463
464
 
464
465
  ## Conflict radar
465
466
 
466
- For each agent with its own branch, Agent Manager works out which files and line ranges its branch changed since it branched, including uncommitted and new files. Agents working in the same repository (the same shared clone or source folder) that touch the same file are flagged:
467
+ For each agent with its own branch, AI Agent Manager works out which files and line ranges its branch changed since it branched, including uncommitted and new files. Agents working in the same repository (the same shared clone or source folder) that touch the same file are flagged:
467
468
 
468
469
  - **same lines**: both edit the same region, within a few lines (likely a merge conflict). Only judged when both branched from the same commit.
469
470
  - **same file**: both edit the file, in different places.
@@ -509,7 +510,7 @@ Fixed rules are cheap but miss context: three identical failures can be a conver
509
510
 
510
511
  ## Checkpoints and forks
511
512
 
512
- At the end of each turn, Agent Manager snapshots the agent's working tree as a commit under `refs/agentmgr/<agent>/<n>`. This doesn't touch the agent's branch, index or files. It records how far the conversation had got alongside. The last 40 are kept (`maxCheckpoints`); deleting the agent removes them.
513
+ At the end of each turn, AI Agent Manager snapshots the agent's working tree as a commit under `refs/agentmgr/<agent>/<n>`. This doesn't touch the agent's branch, index or files. It records how far the conversation had got alongside. The last 40 are kept (`maxCheckpoints`); deleting the agent removes them.
513
514
 
514
515
  **Fork** (or `agentmgr fork <agent> [-c n]`) creates a new agent:
515
516
 
@@ -608,7 +609,7 @@ Or use **Remote access** in the header of the UI on this computer.
608
609
 
609
610
  Agents run Claude Code by default. With OpenAI's Codex CLI installed (`npm i -g @openai/codex`, signed in), an agent can run Codex instead: `agentmgr add --engine codex …`, or the **Engine** picker in New agent (shown once a second engine is installed). Everything else works the same way: worktrees, the brief and questions, the inbox (Codex's approval menus included), token usage, stuck detection, PR tracking, Explain, the backlog and the hub. Codex records no cost, and planning while a repo clones and forks are Claude-only for now. Set `"engine": "codex"` in config.json to make it the default; `engines.codex.bin` points at another binary. On the hub, each machine reports the engines it has (`engine:codex`), so shared backlog work can ask for one. See [docs/engines.md](docs/engines.md).
610
611
 
611
- Google's **Antigravity CLI** (`agy`, signed in with your Google account) works the same way: `--engine antigravity`. It replaced Gemini CLI for personal Google accounts in June 2026. Agent Manager adopts the conversation agy starts, resumes it with `--conversation`, reads token usage, commands and file writes from its transcript, shows its permission menus (commands, file changes, access outside the folder) in the inbox, pre-trusts agent folders in agy's settings, and runs Explain in agy's print mode. agy asks before writing the brief too, unless the agent runs in `acceptEdits` mode. `engines.antigravity.bin` points at another binary.
612
+ Google's **Antigravity CLI** (`agy`, signed in with your Google account) works the same way: `--engine antigravity`. It replaced Gemini CLI for personal Google accounts in June 2026. AI Agent Manager adopts the conversation agy starts, resumes it with `--conversation`, reads token usage, commands and file writes from its transcript, shows its permission menus (commands, file changes, access outside the folder) in the inbox, pre-trusts agent folders in agy's settings, and runs Explain in agy's print mode. agy asks before writing the brief too, unless the agent runs in `acceptEdits` mode. `engines.antigravity.bin` points at another binary.
612
613
 
613
614
  ## Several machines: the hub
614
615
 
@@ -697,8 +698,8 @@ Usage comes from the session transcripts and is refreshed every few seconds whil
697
698
  - **Open shell** (or `agentmgr shell <agent>`) starts `$SHELL -l` in the agent's working folder. It shows as a **Shell 1** tab next to the **Claude** tab in the terminal pane; **+ Shell** (or `agentmgr shell <agent> -n 2`) opens more, and **×** on a tab closes that shell. Shells live as long as the daemon and don't start Claude.
698
699
  - **+ Browser** opens a tab on an address: a port (`5173`), a local address (`localhost:3000/app`), something on the machine's network (`10.0.0.7:8080`, `staging.internal`) or any site (`example.com`, which defaults to https). Tabs are kept with the agent, so every device sees them, and **×** closes one.
699
700
  - **On this computer** the tab loads the address directly. Many public sites refuse to appear inside another page, so use **↗** for those.
700
- - **From another device** (remote access), Agent Manager gives each site its own HTTPS port. That port relays to the site as the machine sees it, so you can check from your phone what the agent built or what the machine can reach. Absolute links, redirects and hot-reload WebSockets on the same site keep working, and the site's "don't show me in a frame" headers are dropped. A link or redirect to a different site leaves the relay.
701
- - **Safety:** the relay serves only signed-in devices. It never relays to Agent Manager's own ports on any of the machine's addresses, and it removes Agent Manager's sign-in cookie before forwarding. Sites other than localhost must present a valid certificate. The relays close when remote access is turned off.
701
+ - **From another device** (remote access), AI Agent Manager gives each site its own HTTPS port. That port relays to the site as the machine sees it, so you can check from your phone what the agent built or what the machine can reach. Absolute links, redirects and hot-reload WebSockets on the same site keep working, and the site's "don't show me in a frame" headers are dropped. A link or redirect to a different site leaves the relay.
702
+ - **Safety:** the relay serves only signed-in devices. It never relays to AI Agent Manager's own ports on any of the machine's addresses, and it removes AI Agent Manager's sign-in cookie before forwarding. Sites other than localhost must present a valid certificate. The relays close when remote access is turned off.
702
703
  - If a tab stays blank on a device, open it once with **↗** and accept the certificate there.
703
704
  - **Ask Claude** (or `agentmgr ask`) creates a `general` agent. All general sessions share `~/.agentmanager/general`, so Claude's folder-trust prompt only appears the first time.
704
705
 
@@ -762,7 +763,7 @@ npm test # unit tests + end-to-end daemon tests against a stub claude (test/f
762
763
 
763
764
  - How the code fits together: [docs/architecture.md](docs/architecture.md). Extending it from a separate package (policies, audit, routes, engines, hub sign-in): [docs/plugins.md](docs/plugins.md). How to contribute: [CONTRIBUTING.md](CONTRIBUTING.md). Changes per release: [CHANGELOG.md](CHANGELOG.md).
764
765
  - Security model and how to report a problem privately: [SECURITY.md](SECURITY.md).
765
- - Agent Manager sends no telemetry.
766
- - Agent Manager runs the agent CLIs you install and sign in to (Claude Code, Codex, Antigravity) with your own accounts. Use them within your provider's terms.
766
+ - AI Agent Manager sends no telemetry. The only request it makes on its own behalf is **Check for updates**, when you click it.
767
+ - AI Agent Manager runs the agent CLIs you install and sign in to (Claude Code, Codex, Antigravity) with your own accounts. Use them within your provider's terms.
767
768
  - Maintained in spare time. Issues and pull requests are welcome; replies may take a few days.
768
769
  - Licensed under the [Apache License 2.0](LICENSE).
package/SECURITY.md CHANGED
@@ -4,9 +4,9 @@
4
4
 
5
5
  Please report security issues privately through GitHub: **Security → Report a vulnerability** on this repository. Don't open a public issue. You'll get a reply within a few days, and a fix or a plan before anything is disclosed.
6
6
 
7
- ## What Agent Manager is, from a security point of view
7
+ ## What AI Agent Manager is, from a security point of view
8
8
 
9
- Agent Manager runs coding agents (Claude Code, Codex, Antigravity) **as your user, on your machine**, in real terminals. Anyone who can drive Agent Manager can run commands as you. The design goal is that only you can drive it, from this machine, unless you deliberately open it up.
9
+ AI Agent Manager runs coding agents (Claude Code, Codex, Antigravity) **as your user, on your machine**, in real terminals. Anyone who can drive AI Agent Manager can run commands as you. The design goal is that only you can drive it, from this machine, unless you deliberately open it up.
10
10
 
11
11
  ### Trust boundaries
12
12
 
@@ -16,7 +16,7 @@ Agent Manager runs coding agents (Claude Code, Codex, Antigravity) **as your use
16
16
  | Remote access (`https://<ip>:4718`) | Nobody: **off until you run `agentmgr remote on`** | HTTPS with a certificate made on this machine. Every API call needs a signed-in device session (sign-in with the access token or its QR code; sessions last 30 days, can be revoked one by one, and rotating the token signs everyone out). Only this machine's own addresses are accepted as `Host`. Turning remote access on or off, the token, devices and shutdown are local-only. |
17
17
  | Hub (`agentmgr hub serve`, port 4720) | This machine and its Tailscale address | One admin token (or Google sign-in limited to listed emails); browsers get a 30-day session cookie. Plain `http://` is accepted only on loopback or Tailscale, which encrypts traffic; anything else must be `https://`. |
18
18
  | A machine joined to a hub | The hub, over a connection the machine opens | Ed25519 key per machine (private key in `~/.agentmanager/hub-link.json`, mode 600); each connection signs a fresh challenge. The machine decides what the hub may do (`hub.allow`, default `view` only), checks every request itself, matches agents by exact ID, runs only a fixed list of actions (never deletes) and logs each request and refusal to `~/.agentmanager/hub-audit.log`. |
19
- | Site relays (browser tabs from another device) | Signed-in remote devices | Never relay to Agent Manager's own ports; its sign-in cookie is stripped before forwarding; sites other than localhost must present a valid certificate; relays close when remote access is turned off. |
19
+ | Site relays (browser tabs from another device) | Signed-in remote devices | Never relay to AI Agent Manager's own ports; its sign-in cookie is stripped before forwarding; sites other than localhost must present a valid certificate; relays close when remote access is turned off. |
20
20
  | CLI sign-in callbacks | The UI | Delivered only to the loopback port the sign-in link itself named. |
21
21
 
22
22
  ### What is stored, and where
@@ -30,12 +30,12 @@ Everything lives in `~/.agentmanager/` (or `$AGENTMGR_HOME`):
30
30
  - `browser-profile/`: the live browser's Chrome profile. Sites you sign in to there stay signed in, as in any browser.
31
31
  - `scrollback/`, `briefs/`, `state.json`: terminal output and agent state. These can contain anything an agent printed, including secrets it read.
32
32
 
33
- Agent Manager sends **no telemetry**. It talks to the network only for what you set up: git remotes, GitHub (`gh`), Slack, a hub you joined, and the agents' own model providers.
33
+ AI Agent Manager sends **no telemetry**. It talks to the network only for what you set up: git remotes, GitHub (`gh`), Slack, a hub you joined, and the agents' own model providers. **Check for updates** in the About box (or `agentmgr about --check`) asks the npm registry for the newest version, only when you click it.
34
34
 
35
35
  ### Things to be aware of
36
36
 
37
- - **Agents act as you.** Each agent's permission mode (for Claude: `default`, `acceptEdits`, `plan`, `auto`, `bypassPermissions`) is passed to the engine; Agent Manager doesn't sandbox agents beyond that. Run agents in folders and with modes you'd be comfortable running the engine in by hand.
38
- - **Other processes running as your user** can call the local API, just as they could run `claude` themselves. Agent Manager doesn't defend against malware already running as you.
37
+ - **Agents act as you.** Each agent's permission mode (for Claude: `default`, `acceptEdits`, `plan`, `auto`, `bypassPermissions`) is passed to the engine; AI Agent Manager doesn't sandbox agents beyond that. Run agents in folders and with modes you'd be comfortable running the engine in by hand.
38
+ - **Other processes running as your user** can call the local API, just as they could run `claude` themselves. AI Agent Manager doesn't defend against malware already running as you.
39
39
  - **The AI judge** sends short summaries of an agent's screen, activity and brief to the same engine the agents use (by default Claude `haiku`), as separate calls. Turn it off with `agentmgr judge off`.
40
40
  - **Remote access on an untrusted network** (café Wi-Fi) exposes the sign-in page on your local network. Leave it off there, or use Tailscale and the hub instead.
41
41
 
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- // The hub on its own, for hosts without the rest of Agent Manager (Railway, a container).
2
+ // The hub on its own, for hosts without the rest of AI Agent Manager (Railway, a container).
3
3
  // Locally, "agentmgr hub serve" does the same.
4
4
  import { startHub } from '../src/hub.js';
5
5
 
@@ -1,6 +1,6 @@
1
1
  # Architecture
2
2
 
3
- Agent Manager is one long-running **daemon** per machine. It owns every agent's terminal and state. The **web UI** and the **CLI** are clients of its HTTP and WebSocket API. Optionally, daemons on several machines connect out to a **hub**.
3
+ AI Agent Manager is one long-running **daemon** per machine. It owns every agent's terminal and state. The **web UI** and the **CLI** are clients of its HTTP and WebSocket API. Optionally, daemons on several machines connect out to a **hub**.
4
4
 
5
5
  ```
6
6
  browser (UI) ──┐ ┌── claude / codex / agy in PTYs (one per agent, plus shells)
@@ -4,7 +4,7 @@ Status: phases 1–3 built (0.3, branch `v0.3-fleet`) · Phases 4–5: planned
4
4
 
5
5
  ## Problem
6
6
 
7
- Agent Manager runs every agent on the machine its daemon runs on. Once you have agents on more than one machine (a Mac mini that is always on, a laptop, a cloud VM with more cores, a teammate's box), you get one UI per machine, one inbox per machine, and a backlog that can't use spare capacity elsewhere. Overlaps between agents on different machines go unnoticed until merge time.
7
+ AI Agent Manager runs every agent on the machine its daemon runs on. Once you have agents on more than one machine (a Mac mini that is always on, a laptop, a cloud VM with more cores, a teammate's box), you get one UI per machine, one inbox per machine, and a backlog that can't use spare capacity elsewhere. Overlaps between agents on different machines go unnoticed until merge time.
8
8
 
9
9
  ## Goals
10
10
 
package/docs/engines.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  Status: phase 1, Codex and Antigravity built. Gemini CLI dropped: since June 18, 2026 it no longer serves personal Google accounts, and Antigravity CLI (`agy`, `src/engines/antigravity.js`, checked against 1.4.3, fixtures in `test/fixtures/antigravity/`) replaced it. 0.4 (branch `v0.4-engines`) · 2026-10-18. Targets the current releases of each CLI, checked in phase 1.
4
4
 
5
- Agent Manager runs every agent with Claude Code. If Anthropic has an outage, a rate limit hits, or prices change, every agent stops at once. Running agents on other engines as well makes the fleet resilient across providers, and lets each task use the engine that suits it.
5
+ AI Agent Manager runs every agent with Claude Code. If Anthropic has an outage, a rate limit hits, or prices change, every agent stops at once. Running agents on other engines as well makes the fleet resilient across providers, and lets each task use the engine that suits it.
6
6
 
7
7
  ## Goals
8
8
 
@@ -14,7 +14,7 @@ Agent Manager runs every agent with Claude Code. If Anthropic has an outage, a r
14
14
  ## Non-goals
15
15
 
16
16
  - **Moving a live conversation between engines.** Conversations stay with their engine. A task can continue on another engine through a handoff (below), which carries the work but not the chat history.
17
- - **Editor-based agents** (Antigravity, Cursor, and so on). Agent Manager drives command-line agents in a terminal.
17
+ - **Editor-based agents** (Antigravity, Cursor, and so on). AI Agent Manager drives command-line agents in a terminal.
18
18
  - **Running models directly through APIs.** Each engine keeps its own CLI, sign-in, tools and safety settings.
19
19
 
20
20
  ## What is Claude-specific today
@@ -91,9 +91,9 @@ Gemini CLI flags are checked against 0.64.0 (`test/fixtures/gemini/help-0.64.0.t
91
91
 
92
92
  ### Permission modes
93
93
 
94
- Agent Manager's three modes map to each engine's own approval settings. The hub's rule stays: no mode that skips all prompts can be started from the hub.
94
+ AI Agent Manager's three modes map to each engine's own approval settings. The hub's rule stays: no mode that skips all prompts can be started from the hub.
95
95
 
96
- | Agent Manager | Claude Code | Codex CLI | Gemini CLI |
96
+ | AI Agent Manager | Claude Code | Codex CLI | Gemini CLI |
97
97
  | --- | --- | --- | --- |
98
98
  | `default` (ask) | `default` | `--sandbox workspace-write --ask-for-approval on-request` (0.163 only has `on-request` and `never`) | `--approval-mode default` |
99
99
  | `acceptEdits` | `acceptEdits` | the same: edits inside the workspace never ask | `--approval-mode auto_edit` |
@@ -107,7 +107,7 @@ The brief and questions files are what make an agent reviewable from the inbox,
107
107
  - **Instructions:** today they go in `--append-system-prompt`. Codex and Gemini have no per-launch system prompt flag, so the instructions go in the first message of a new session, and are repeated briefly on resume (in the resume message where the engine accepts one). Writing them into the repo's `AGENTS.md`/`GEMINI.md` is rejected: that would change the user's repo.
108
108
  - **Writing outside the repo:** Codex's sandbox only writes inside the workspace by default. Options, in order of preference:
109
109
  1. Allow the brief folder as an extra writable root (`--add-dir` or `writable_roots`).
110
- 2. Have the agent write `.agentmgr/BRIEF.md` inside its worktree, add `.agentmgr/` to the worktree's local git excludes (`.git/info/exclude` of the worktree, never the repo's `.gitignore`), and have Agent Manager read it from there.
110
+ 2. Have the agent write `.agentmgr/BRIEF.md` inside its worktree, add `.agentmgr/` to the worktree's local git excludes (`.git/info/exclude` of the worktree, never the repo's `.gitignore`), and have AI Agent Manager read it from there.
111
111
  - **Parsing** (`parseBrief`, `parseQuestions`) is already engine-neutral.
112
112
 
113
113
  ### Usage, activity and stuck detection
package/docs/plugins.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Plugins
2
2
 
3
- A plugin extends Agent Manager from a separate package, without changing its code: add policies that can refuse actions, listen to what happens (for an audit trail), serve extra API routes, add an agent engine, or (on the hub) add a sign-in method.
3
+ A plugin extends AI Agent Manager from a separate package, without changing its code: add policies that can refuse actions, listen to what happens (for an audit trail), serve extra API routes, add an agent engine, or (on the hub) add a sign-in method.
4
4
 
5
5
  ## Loading
6
6
 
@@ -10,7 +10,7 @@ A plugin extends Agent Manager from a separate package, without changing its cod
10
10
  { "plugins": ["@acme/agentmanager-policy", "./plugins/audit.js"] }
11
11
  ```
12
12
 
13
- Package names are resolved from Agent Manager's install; paths are relative to `~/.agentmanager` (`~` works too).
13
+ Package names are resolved from AI Agent Manager's install; paths are relative to `~/.agentmanager` (`~` works too).
14
14
 
15
15
  **Hub:** `AGENTMGR_HUB_PLUGINS=@acme/agentmanager-sso,./plugins/hub-audit.js` (paths relative to the hub's state folder).
16
16
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ai-agentmanager",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "description": "Run background coding agents (Claude Code, Codex) per intent, and keep track of them from a web UI, your phone or the CLI",
5
5
  "keywords": [
6
6
  "claude",
@@ -19,6 +19,7 @@
19
19
  "bugs": {
20
20
  "url": "https://github.com/jdmunta/ai-agentmanager-core/issues"
21
21
  },
22
+ "author": "Jagadesh Munta (https://github.com/jdmunta)",
22
23
  "license": "Apache-2.0",
23
24
  "type": "module",
24
25
  "bin": {
@@ -33,7 +34,8 @@
33
34
  "README.md",
34
35
  "LICENSE",
35
36
  "NOTICE",
36
- "SECURITY.md"
37
+ "SECURITY.md",
38
+ "CHANGELOG.md"
37
39
  ],
38
40
  "scripts": {
39
41
  "start": "node bin/agentmgr.js serve",
@@ -47,10 +49,10 @@
47
49
  "@xterm/addon-fit": "^0.11.0",
48
50
  "@xterm/xterm": "^6.0.0",
49
51
  "commander": "^15.0.0",
50
- "dompurify": "^3.4.16",
52
+ "dompurify": "^3.4.17",
51
53
  "express": "^5.2.1",
52
54
  "marked": "^18.0.14",
53
- "mermaid": "^11.17.2",
55
+ "mermaid": "^12.2.0",
54
56
  "node-pty": "^1.1.0",
55
57
  "qrcode": "^1.5.4",
56
58
  "ws": "^8.22.0"
package/public/app.js CHANGED
@@ -464,7 +464,7 @@ function resultsCard(a) {
464
464
  const [icon, label] = OUTCOME[r.outcome] ?? ['🔄', r.outcome];
465
465
  const item = (x, cls) => `<li class="${cls}">${x.url ? `<a href="${esc(x.url)}" target="_blank" rel="noopener">${md(x.text).replace(/^<p>|<\/p>\s*$/g, '')}</a>` : md(x.text).replace(/^<p>|<\/p>\s*$/g, '')}</li>`;
466
466
  return `<div class="results ${r.outcome.replace(' ', '-')}">
467
- <h4>Results <span class="outcome">${icon} ${label}</span>${r.fromAgent ? '' : ' <span class="hint">(from what Agent Manager can see; the agent adds its own when it next updates its brief)</span>'}</h4>
467
+ <h4>Results <span class="outcome">${icon} ${label}</span>${r.fromAgent ? '' : ' <span class="hint">(from what AI Agent Manager can see; the agent adds its own when it next updates its brief)</span>'}</h4>
468
468
  ${r.blockers.length ? `<ul class="blockers">${r.blockers.map((x) => item(x, 'blocker')).join('')}</ul>` : ''}
469
469
  ${r.delivered.length ? `<ul class="delivered">${r.delivered.map((x) => item(x, 'delivered')).join('')}</ul>` : '<p class="hint">Nothing delivered yet.</p>'}
470
470
  ${r.notes ? `<div class="md">${md(r.notes)}</div>` : ''}
@@ -629,7 +629,7 @@ function projectGroups(list) {
629
629
 
630
630
  function renderList() {
631
631
  const waiting = [...agents.values()].filter((a) => a.runtime.attention).length;
632
- document.title = waiting ? `(${waiting}) Agent Manager` : 'Agent Manager';
632
+ document.title = waiting ? `(${waiting}) AI Agent Manager` : 'AI Agent Manager';
633
633
  // "@cc" shows one project; any other text also matches shortcuts.
634
634
  const at = filterText.startsWith('@') ? filterText.slice(1) : null;
635
635
  const list = [...agents.values()].filter((a) =>
@@ -1995,7 +1995,7 @@ function renderImport() {
1995
1995
  .join('')}</div>`,
1996
1996
  )
1997
1997
  .join('')
1998
- : `<p class="hint">${importSessions.length ? 'Nothing matches.' : 'No Claude sessions started in a terminal in this period (Agent Manager\'s own agents are left out).'}</p>`;
1998
+ : `<p class="hint">${importSessions.length ? 'Nothing matches.' : 'No Claude sessions started in a terminal in this period (AI Agent Manager\'s own agents are left out).'}</p>`;
1999
1999
  const picked = importSessions.filter((s) => importPicked.has(s.sessionId));
2000
2000
  const open = picked.filter((s) => s.inUse).length;
2001
2001
  Object.assign($('#import-go'), {
@@ -2562,8 +2562,8 @@ $('#detail-body').addEventListener('click', async (e) => {
2562
2562
  break;
2563
2563
  case 'delete': {
2564
2564
  const cancelNote = a.status === 'preparing' ? '\n\nThis also cancels the workspace setup (clone / checkout) in progress.' : '';
2565
- if (!confirm(`Delete agent "${a.name}"? Its session IDs will be forgotten by Agent Manager.${cancelNote}`)) return;
2566
- const del = a.worktree && confirm('Also remove the git worktree Agent Manager created for it?\n(Uncommitted changes there will be lost.)');
2565
+ if (!confirm(`Delete agent "${a.name}"? Its session IDs will be forgotten by AI Agent Manager.${cancelNote}`)) return;
2566
+ const del = a.worktree && confirm('Also remove the git worktree AI Agent Manager created for it?\n(Uncommitted changes there will be lost.)');
2567
2567
  act(async () => {
2568
2568
  const res = await api('DELETE', `/agents/${a.id}${del ? '?deleteWorkspace=1' : ''}`);
2569
2569
  if (res.warnings?.length) toast(res.warnings.join('; '), 6000);
@@ -2730,6 +2730,61 @@ $('#new-form').addEventListener('submit', async (e) => {
2730
2730
 
2731
2731
  // ------------------------------------------------------------------ ask (general session)
2732
2732
 
2733
+ // ---------------------------------------------------------------- about
2734
+ const aboutDialog = $('#about-dialog');
2735
+ let aboutInfo = null;
2736
+ async function loadAbout() {
2737
+ aboutInfo = await api('GET', '/about');
2738
+ $('#about-btn').textContent = `v${aboutInfo.version}`;
2739
+ return aboutInfo;
2740
+ }
2741
+ loadAbout().catch(() => {});
2742
+ function renderAbout(a) {
2743
+ const link = (url, text = url.replace(/^https:\/\//, '')) => `<a href="${esc(url)}" target="_blank" rel="noopener">${esc(text)}</a>`;
2744
+ const when = (iso) => new Date(iso).toLocaleString();
2745
+ const rows = [
2746
+ ['Version', `${esc(a.version)}${a.releaseDate ? ` <span class="hint">released ${esc(a.releaseDate)}</span>` : ''}`],
2747
+ ['Author', esc(a.author ?? '—')],
2748
+ ['License', esc(a.license ?? '—')],
2749
+ ['Install', `<code>npm install -g ${esc(a.package)}</code>, then <code>${esc(a.command ?? 'agentmgr')}</code>`],
2750
+ ['Code', a.links.repository ? link(a.links.repository) : '—'],
2751
+ ['Releases', a.links.releases ? link(a.links.releases, 'Release notes') : '—'],
2752
+ ['Report a problem', a.links.issues ? link(a.links.issues, 'GitHub issues') : '—'],
2753
+ null,
2754
+ ['Daemon', `pid ${esc(a.daemon.pid)} · port ${esc(a.daemon.port ?? '—')} · running since ${esc(when(a.daemon.startedAt))}`],
2755
+ ['Data folder', `<code>${esc(a.daemon.home ?? '—')}</code>`],
2756
+ ['Node.js', esc(a.daemon.node)],
2757
+ ['System', esc(a.daemon.platform)],
2758
+ ['Claude Code', `<code>${esc(a.daemon.claudeBin ?? 'claude')}</code>`],
2759
+ ];
2760
+ $('#about-list').innerHTML = rows.map((r) => (r ? `<dt>${r[0]}</dt><dd>${r[1]}</dd>` : '<div class="sep"></div>')).join('');
2761
+ }
2762
+ $('#about-btn').onclick = async () => {
2763
+ $('#about-update').hidden = true;
2764
+ if (aboutInfo) renderAbout(aboutInfo);
2765
+ else $('#about-list').innerHTML = '<dt>Loading…</dt><dd></dd>';
2766
+ aboutDialog.showModal();
2767
+ try {
2768
+ renderAbout(await loadAbout()); // fresh daemon details (uptime, pid) each time it opens
2769
+ } catch (err) {
2770
+ $('#about-list').innerHTML = `<dt>Error</dt><dd>${esc(err.message)}</dd>`;
2771
+ }
2772
+ };
2773
+ $('#about-close').onclick = () => aboutDialog.close();
2774
+ $('#about-check').onclick = async () => {
2775
+ const out = $('#about-update');
2776
+ out.hidden = false;
2777
+ out.textContent = 'Checking…';
2778
+ try {
2779
+ const r = await api('GET', '/about/latest');
2780
+ out.innerHTML = r.newer
2781
+ ? `Version ${esc(r.latest)} is available. Update with <code>npm install -g ${esc(aboutInfo?.package ?? 'ai-agentmanager')}@latest</code>, then <code>agentmgr restart</code>.`
2782
+ : `You have the latest version (${esc(r.current)}).`;
2783
+ } catch (err) {
2784
+ out.textContent = `Couldn't check: ${err.message}`;
2785
+ }
2786
+ };
2787
+
2733
2788
  const askDialog = $('#ask-dialog');
2734
2789
  $('#ask-btn').onclick = () => {
2735
2790
  $('#ask-error').hidden = true;
@@ -4,7 +4,7 @@
4
4
  <meta charset="utf-8" />
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1" />
6
6
  <meta name="referrer" content="no-referrer" />
7
- <title>Agent Manager Hub</title>
7
+ <title>AI Agent Manager Hub</title>
8
8
  <link rel="icon" href="data:image/svg+xml,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 16 16%22%3E%3Cpath d=%22M8 1l7 7-7 7-7-7z%22 fill=%22%233b5bdb%22/%3E%3C/svg%3E" />
9
9
  <link rel="stylesheet" href="/vendor/xterm/css/xterm.css" />
10
10
  <link rel="stylesheet" href="/hub.css" />
@@ -13,7 +13,7 @@
13
13
  <header class="topbar">
14
14
  <div class="brand">
15
15
  <span class="logo" aria-hidden="true">◆</span>
16
- <h1>Agent Manager <span class="muted">Hub</span></h1>
16
+ <h1>AI Agent Manager <span class="muted">Hub</span></h1>
17
17
  <span id="hub-name" class="muted"></span>
18
18
  </div>
19
19
  <div class="top-actions" id="top-actions" hidden>
@@ -62,7 +62,7 @@
62
62
  <dialog id="invite-dialog">
63
63
  <form method="dialog" class="dialog">
64
64
  <h2>Add a machine</h2>
65
- <p class="muted">On the Mac to add (with Agent Manager installed), run:</p>
65
+ <p class="muted">On the Mac to add (with AI Agent Manager installed), run:</p>
66
66
  <div class="cmd"><code id="invite-cmd">…</code><button type="button" id="copy-btn" class="ghost">Copy</button></div>
67
67
  <p class="muted small" id="invite-expiry"></p>
68
68
  <p class="error small" id="invite-warning" hidden></p>
package/public/index.html CHANGED
@@ -4,7 +4,7 @@
4
4
  <meta charset="utf-8" />
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
6
6
  <meta name="apple-mobile-web-app-capable" content="yes" />
7
- <title>Agent Manager</title>
7
+ <title>AI Agent Manager</title>
8
8
  <link rel="icon" href="data:image/svg+xml,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 16 16%22%3E%3Cpath d=%22M8 1l7 7-7 7-7-7z%22 fill=%22%233b5bdb%22/%3E%3C/svg%3E" />
9
9
  <link rel="stylesheet" href="/vendor/xterm/css/xterm.css" />
10
10
  <link rel="stylesheet" href="/style.css" />
@@ -13,13 +13,14 @@
13
13
  <header class="topbar">
14
14
  <div class="brand">
15
15
  <span class="logo" aria-hidden="true">◆</span>
16
- <h1>Agent Manager</h1>
16
+ <h1>AI Agent Manager</h1>
17
+ <button id="about-btn" class="about-btn" type="button" title="About AI Agent Manager: version, release, links">About</button>
17
18
  <span id="conn" class="conn" title="Daemon connection">connecting…</span>
18
19
  </div>
19
20
  <div class="top-actions">
20
21
  <input id="filter" type="search" placeholder="Filter agents" aria-label="Filter agents" />
21
22
  <button id="alerts-btn" class="local-only" title="Slack alerts when an agent needs you">Alerts</button>
22
- <button id="remote-btn" class="local-only" title="Open Agent Manager from your phone">Remote access</button>
23
+ <button id="remote-btn" class="local-only" title="Open AI Agent Manager from your phone">Remote access</button>
23
24
  <button id="ask-btn" title="A general Claude session with no repo or folder">Ask Claude</button>
24
25
  <button id="new-btn" class="primary">+ New agents</button>
25
26
  </div>
@@ -79,12 +80,12 @@
79
80
  </form>
80
81
  <div id="browser-hint" class="browser-hint" hidden>Blank? Many sites refuse to show inside another page. Open it with ↗ instead.</div>
81
82
  <div id="browser-cert" class="browser-start" hidden>
82
- <p><b>One more step on this device.</b> Each browser tab goes through its own port, and this device hasn't accepted Agent Manager's certificate for this one yet.</p>
83
- <p>Open it, accept the warning the same way you did for Agent Manager (Safari: Show Details → visit this website), then come back here. You only do this once per site; it keeps its port.</p>
83
+ <p><b>One more step on this device.</b> Each browser tab goes through its own port, and this device hasn't accepted AI Agent Manager's certificate for this one yet.</p>
84
+ <p>Open it, accept the warning the same way you did for AI Agent Manager (Safari: Show Details → visit this website), then come back here. You only do this once per site; it keeps its port.</p>
84
85
  <div><a id="browser-cert-open" class="button primary small" target="_blank" rel="noopener">Open and accept</a> <button type="button" id="browser-cert-retry" class="small">Done, load it</button></div>
85
86
  </div>
86
87
  <div id="browser-start" class="browser-start" hidden>
87
- <p>Open a page as this machine sees it: the agent's dev server, a site on its network or VPN, or any address. It works from your phone or laptop too: Agent Manager relays it over its own secure connection, for signed-in devices only.</p>
88
+ <p>Open a page as this machine sees it: the agent's dev server, a site on its network or VPN, or any address. It works from your phone or laptop too: AI Agent Manager relays it over its own secure connection, for signed-in devices only.</p>
88
89
  <div id="browser-suggest"></div>
89
90
  </div>
90
91
  <div id="browser-frames" class="browser-frames"></div>
@@ -337,6 +338,17 @@
337
338
  </div>
338
339
  </dialog>
339
340
 
341
+ <dialog id="about-dialog" class="about-dialog">
342
+ <h2>AI Agent Manager</h2>
343
+ <p class="hint">Run and track background coding agents, one per intent.</p>
344
+ <dl id="about-list" class="about-list"></dl>
345
+ <p id="about-update" class="hint" hidden></p>
346
+ <div class="dialog-actions">
347
+ <button type="button" id="about-check" class="ghost" title="Asks the npm registry for the newest version">Check for updates</button>
348
+ <button type="button" id="about-close" class="primary">Close</button>
349
+ </div>
350
+ </dialog>
351
+
340
352
  <script type="module" src="/app.js"></script>
341
353
  </body>
342
354
  </html>
package/public/share.js CHANGED
@@ -9,7 +9,7 @@ $('#print').onclick = () => window.print();
9
9
 
10
10
  async function main() {
11
11
  const res = await fetch(`/api/agents/${encodeURIComponent(agentId)}`, { credentials: 'same-origin' });
12
- if (!res.ok) throw new Error(res.status === 401 ? 'Sign in to Agent Manager first, then reopen this page.' : `HTTP ${res.status}`);
12
+ if (!res.ok) throw new Error(res.status === 401 ? 'Sign in to AI Agent Manager first, then reopen this page.' : `HTTP ${res.status}`);
13
13
  const a = await res.json();
14
14
  const d = a.docs?.[kind];
15
15
  if (!d?.markdown) throw new Error('This document has not been written yet.');
package/public/style.css CHANGED
@@ -56,6 +56,15 @@ a:hover { text-decoration: underline; }
56
56
  .brand h1 { font-size: 16px; margin: 0; font-weight: 600; }
57
57
  .logo { color: var(--accent); }
58
58
  .conn { font-size: 12px; color: var(--muted); }
59
+ .about-btn { font-size: 12px; padding: 2px 8px; color: var(--muted); background: transparent; border: 1px solid var(--border); border-radius: 999px; font-variant-numeric: tabular-nums; }
60
+ .about-btn:hover, .about-btn:focus-visible { color: var(--text); border-color: var(--accent); }
61
+ .about-dialog { width: min(560px, calc(100vw - 32px)); padding: 20px; }
62
+ .about-dialog h2 { margin: 0 0 2px; font-size: 17px; }
63
+ .about-dialog .hint { margin: 0; }
64
+ .about-list { display: grid; grid-template-columns: max-content minmax(0, 1fr); gap: 6px 16px; margin: 14px 0; font-size: 13px; }
65
+ .about-list dt { color: var(--muted); }
66
+ .about-list dd { margin: 0; min-width: 0; overflow-wrap: anywhere; }
67
+ .about-list .sep { grid-column: 1 / -1; border-top: 1px solid var(--border); margin: 4px 0; }
59
68
  .conn.ok::before { content: '● '; color: var(--green); }
60
69
  .conn.bad::before { content: '● '; color: var(--red); }
61
70
  .top-actions { display: flex; gap: 8px; }
package/src/about.js ADDED
@@ -0,0 +1,70 @@
1
+ // What the About box and "agentmgr about" show: the release, who makes it, where to get help,
2
+ // and how this daemon runs. Nothing here leaves the machine; the update check is a separate,
3
+ // user-triggered request to the npm registry.
4
+ import fs from 'node:fs';
5
+ import os from 'node:os';
6
+ import path from 'node:path';
7
+ import { fileURLToPath } from 'node:url';
8
+
9
+ const root = path.join(path.dirname(fileURLToPath(import.meta.url)), '..');
10
+ const read = (f) => {
11
+ try {
12
+ return fs.readFileSync(path.join(root, f), 'utf8');
13
+ } catch {
14
+ return '';
15
+ }
16
+ };
17
+ const pkg = JSON.parse(read('package.json'));
18
+ const startedAt = new Date().toISOString();
19
+
20
+ /** The date in a changelog heading like "## 1.0.0 (2026-10-25)", or null. */
21
+ export function releaseDate(changelog, version) {
22
+ const esc = version.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
23
+ return changelog.match(new RegExp(`^##\\s+v?${esc}\\s+\\((\\d{4}-\\d{2}-\\d{2})\\)`, 'm'))?.[1] ?? null;
24
+ }
25
+
26
+ const authorName = (a) => (typeof a === 'string' ? a.replace(/\s*[<(].*$/, '') : a?.name) || null;
27
+ const repoUrl = (r) => String((typeof r === 'string' ? r : r?.url) ?? '').replace(/^git\+/, '').replace(/\.git$/, '') || null;
28
+
29
+ export function about({ port, config = {} } = {}) {
30
+ return {
31
+ product: 'AI Agent Manager',
32
+ package: pkg.name,
33
+ command: Object.keys(pkg.bin ?? {})[0] ?? null,
34
+ version: pkg.version,
35
+ releaseDate: releaseDate(read('CHANGELOG.md'), pkg.version),
36
+ author: authorName(pkg.author),
37
+ license: pkg.license,
38
+ links: {
39
+ repository: repoUrl(pkg.repository),
40
+ issues: pkg.bugs?.url ?? null,
41
+ releases: repoUrl(pkg.repository) ? `${repoUrl(pkg.repository)}/releases` : null,
42
+ npm: `https://www.npmjs.com/package/${pkg.name}`,
43
+ },
44
+ daemon: {
45
+ pid: process.pid,
46
+ port: port ?? null,
47
+ home: config.home ?? null,
48
+ startedAt,
49
+ node: process.version,
50
+ platform: `${os.type()} ${os.release()} (${process.arch})`,
51
+ claudeBin: config.claudeBin ?? null,
52
+ },
53
+ };
54
+ }
55
+
56
+ /** The newest version on npm. Only called when someone clicks "Check for updates". */
57
+ export async function latestRelease({ fetchImpl = fetch, timeoutMs = 5000 } = {}) {
58
+ const res = await fetchImpl(`https://registry.npmjs.org/${encodeURIComponent(pkg.name)}/latest`, { signal: AbortSignal.timeout(timeoutMs) });
59
+ if (!res.ok) throw Object.assign(new Error(`npm answered ${res.status}`), { status: 502 });
60
+ const { version } = await res.json();
61
+ return { current: pkg.version, latest: version, newer: newerThan(version, pkg.version) };
62
+ }
63
+
64
+ /** True when semver `a` is newer than `b` (pre-releases count as older than the release). */
65
+ export function newerThan(a, b) {
66
+ const parse = (v) => String(v).split('-')[0].split('.').map(Number);
67
+ const [x, y] = [parse(a), parse(b)];
68
+ for (let i = 0; i < 3; i++) if ((x[i] ?? 0) !== (y[i] ?? 0)) return (x[i] ?? 0) > (y[i] ?? 0);
69
+ return String(b).includes('-') && !String(a).includes('-');
70
+ }
package/src/brief.js CHANGED
@@ -15,7 +15,7 @@ export function briefPath(agentId) {
15
15
  }
16
16
 
17
17
  export const briefPrompt = (file) =>
18
- `Keep a short, living brief of this task in ${file} (Markdown). Agent Manager shows it to the user ` +
18
+ `Keep a short, living brief of this task in ${file} (Markdown). AI Agent Manager shows it to the user ` +
19
19
  'next to your terminal. Start it with one status line, then use exactly these four sections:\n' +
20
20
  'Status: planning | working | blocked | done\n' +
21
21
  'Set "done" only when the task is complete and its checks pass; set "blocked" when you need the user.\n' +
package/src/cli.js CHANGED
@@ -4,6 +4,7 @@ import { Command, Option } from 'commander';
4
4
  import WebSocket from 'ws';
5
5
  import { HOST, PORT } from './config.js';
6
6
  import { api, BASE, ensureDaemon, isRunning } from './client.js';
7
+ import { about, latestRelease } from './about.js';
7
8
 
8
9
  const tty = process.stdout.isTTY;
9
10
  const c = (code) => (s) => (tty ? `\x1b[${code}m${s}\x1b[0m` : String(s));
@@ -855,7 +856,30 @@ export async function main(argv) {
855
856
  console.log(j.enabled ? `AI judge ${green('on')} ${j.calls} of ${j.cap} calls today (on your own engine plan)` : `AI judge ${dim('off')} (fixed rules only; turn on: agentmgr judge on)`);
856
857
  });
857
858
 
858
- const remoteCmd = program.command('remote').description('Open Agent Manager from a phone or another computer on the same network');
859
+ program
860
+ .command('about')
861
+ .description('Version, release date, author, license and links; the running daemon, if any')
862
+ .option('--check', 'also ask the npm registry for the newest version')
863
+ .action(async (opts) => {
864
+ // An older daemon has no /about; this install's own details still apply.
865
+ const a = (await isRunning()) ? await api('GET', '/about').catch(() => about()) : about();
866
+ const row = (k, v) => console.log(`${dim(k.padEnd(13))} ${v}`);
867
+ console.log(`${a.product} ${green(a.version)}${a.releaseDate ? dim(` released ${a.releaseDate}`) : ''}`);
868
+ row('Author', a.author ?? '—');
869
+ row('License', a.license ?? '—');
870
+ row('Package', `${a.package} (npm install -g ${a.package})`);
871
+ row('Code', a.links.repository ?? '—');
872
+ row('Issues', a.links.issues ?? '—');
873
+ if (a.daemon.port) row('Daemon', `pid ${a.daemon.pid}, port ${a.daemon.port}, since ${new Date(a.daemon.startedAt).toLocaleString()}, data in ${a.daemon.home}`);
874
+ else row('Daemon', dim('not running (start: agentmgr ui)'));
875
+ row('Node.js', `${a.daemon.node} on ${a.daemon.platform}`);
876
+ if (opts.check) {
877
+ const r = await latestRelease();
878
+ row('Latest', r.newer ? yellow(`${r.latest} is available: npm install -g ${a.package}@latest`) : `${r.latest} (up to date)`);
879
+ }
880
+ });
881
+
882
+ const remoteCmd = program.command('remote').description('Open AI Agent Manager from a phone or another computer on the same network');
859
883
  const printRemote = async (st, { qr = true } = {}) => {
860
884
  if (!st.enabled || !st.running) return console.log(`remote access ${dim('off')} (turn on: agentmgr remote on)`);
861
885
  console.log(`remote access ${green('on')} HTTPS port ${st.port}`);
package/src/conflicts.js CHANGED
@@ -95,11 +95,11 @@ export function conflictMessage(self, group) {
95
95
  const what = files ? `is changing the same files as you in this repository: ${files}.` : `is working on related code in this repository.`;
96
96
  const order =
97
97
  group.mergeFirst === self.id
98
- ? `Agent Manager suggests your change lands first; ${group.agent.name} will rebase onto it.`
99
- : `Agent Manager suggests ${group.agent.name}'s change lands first: when it does, rebase onto it and resolve the overlap.`;
98
+ ? `AI Agent Manager suggests your change lands first; ${group.agent.name} will rebase onto it.`
99
+ : `AI Agent Manager suggests ${group.agent.name}'s change lands first: when it does, rebase onto it and resolve the overlap.`;
100
100
  const ai = group.ai?.reason ? ` Why this matters: ${group.ai.reason}${group.ai.advice ? ` ${group.ai.advice}` : ''}` : '';
101
101
  return (
102
- `Heads-up from Agent Manager: another agent, ${group.agent.name}, ${what} ${order}${ai} ` +
102
+ `Heads-up from AI Agent Manager: another agent, ${group.agent.name}, ${what} ${order}${ai} ` +
103
103
  `Keep your edits in those files focused, avoid unrelated reformatting there, and mention the overlap in your PR description.`
104
104
  );
105
105
  }
@@ -14,7 +14,7 @@ import path from 'node:path';
14
14
  import { norm, tailOf } from '../stuck.js';
15
15
  import { extractRefs } from '../tracker.js';
16
16
 
17
- // ANTIGRAVITY_CLI_HOME is Agent Manager's own override (tests); agy itself always uses this folder.
17
+ // ANTIGRAVITY_CLI_HOME is AI Agent Manager's own override (tests); agy itself always uses this folder.
18
18
  const home = () => process.env.ANTIGRAVITY_CLI_HOME || path.join(os.homedir(), '.gemini', 'antigravity-cli');
19
19
  const UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;
20
20
  const transcript = (id) => path.join(home(), 'brain', id, '.system_generated', 'logs', 'transcript_full.jsonl');
@@ -155,7 +155,7 @@ export default {
155
155
 
156
156
  /** Full arguments for a launch. Codex takes options after `resume`, and instructions only as text. */
157
157
  launchArgs({ extra = [], permissionMode, model, writableDir, instructions, sessionId, resume, prompt }) {
158
- // Inline mode keeps the conversation in the terminal's scrollback, which Agent Manager replays.
158
+ // Inline mode keeps the conversation in the terminal's scrollback, which AI Agent Manager replays.
159
159
  const opts = [...extra, '--no-alt-screen', ...(MODES[permissionMode] ?? MODES.default)];
160
160
  if (model) opts.push('--model', model);
161
161
  if (writableDir) opts.push('--add-dir', writableDir);
@@ -1,4 +1,4 @@
1
- // The agent engines Agent Manager can run. Each agent records its engine; agents from
1
+ // The agent engines AI Agent Manager can run. Each agent records its engine; agents from
2
2
  // before engines existed are Claude Code agents.
3
3
  import fs from 'node:fs';
4
4
  import path from 'node:path';
package/src/git.js CHANGED
@@ -278,9 +278,9 @@ export async function changes(dir, baseSha) {
278
278
  }
279
279
 
280
280
  const SNAPSHOT_ENV = {
281
- GIT_AUTHOR_NAME: 'Agent Manager',
281
+ GIT_AUTHOR_NAME: 'AI Agent Manager',
282
282
  GIT_AUTHOR_EMAIL: 'agentmgr@localhost',
283
- GIT_COMMITTER_NAME: 'Agent Manager',
283
+ GIT_COMMITTER_NAME: 'AI Agent Manager',
284
284
  GIT_COMMITTER_EMAIL: 'agentmgr@localhost',
285
285
  };
286
286
 
package/src/inbox.js CHANGED
@@ -12,7 +12,7 @@ export const questionsPath = (agentId) => path.join(briefDir(agentId), QUESTIONS
12
12
  export const questionsPrompt = (file) =>
13
13
  `When you need a decision from the user that doesn't have to stop all your work (a choice between options, ` +
14
14
  `a preference, a missing detail), don't wait in the terminal: add it to ${file} and keep going on whatever you can ` +
15
- 'do meanwhile, using your best default. Agent Manager collects these into the user\'s inbox. One question per heading, ' +
15
+ 'do meanwhile, using your best default. AI Agent Manager collects these into the user\'s inbox. One question per heading, ' +
16
16
  'never edit or remove earlier ones:\n' +
17
17
  '## <the question, one line>\n' +
18
18
  'Options: <option A> | <option B> (optional)\n' +
package/src/manager.js CHANGED
@@ -297,7 +297,7 @@ export class AgentManager extends EventEmitter {
297
297
 
298
298
  /**
299
299
  * The end result / status / blockers for an agent: what the agent wrote under
300
- * "## Results", plus what Agent Manager knows (PRs, checks, prompts, errors).
300
+ * "## Results", plus what AI Agent Manager knows (PRs, checks, prompts, errors).
301
301
  */
302
302
  results(agent, rt, prs) {
303
303
  const r = agent.brief?.results;
@@ -749,7 +749,7 @@ export class AgentManager extends EventEmitter {
749
749
  */
750
750
  start(ref, { fresh = false, sessionId } = {}) {
751
751
  const agent = this.mustGet(ref);
752
- if (this.closing) throw Object.assign(new Error('Agent Manager is shutting down'), { status: 503 });
752
+ if (this.closing) throw Object.assign(new Error('AI Agent Manager is shutting down'), { status: 503 });
753
753
  const live = this.runtimes.get(agent.id);
754
754
  if (!live?.pty) {
755
755
  this.plugins.check('agent.start', { agent: this.auditView(agent), fresh });
@@ -1432,7 +1432,7 @@ export class AgentManager extends EventEmitter {
1432
1432
 
1433
1433
  // ------------------------------------------------- sessions started elsewhere
1434
1434
 
1435
- /** Claude Code sessions started outside Agent Manager (in a terminal), to continue here. */
1435
+ /** Claude Code sessions started outside AI Agent Manager (in a terminal), to continue here. */
1436
1436
  scanSessions({ days = 30 } = {}) {
1437
1437
  const known = new Set(this.store.list().flatMap((a) => a.sessions.map((x) => x.id)));
1438
1438
  return scanClaudeSessions({ days: Math.max(1, Math.min(365, Number(days) || 30)), known, excludeDir: paths.home });
package/src/notify.js CHANGED
@@ -269,7 +269,7 @@ export class Notifier {
269
269
  const link = this.linkFor(agent.id);
270
270
  const intent = n.includeIntent && agent.intent ? `\n> ${agent.intent.replace(/\s+/g, ' ').slice(0, 200)}` : '';
271
271
  const prio = agent.priority <= 1 ? `[P${agent.priority}] ` : '';
272
- const text = `${icon} ${prio ? `*${prio.trim()}* ` : ''}*${agent.name}*${project} ${what}${intent}${link ? `\n<${link}|Open in Agent Manager>` : ''}`;
272
+ const text = `${icon} ${prio ? `*${prio.trim()}* ` : ''}*${agent.name}*${project} ${what}${intent}${link ? `\n<${link}|Open in AI Agent Manager>` : ''}`;
273
273
  const plain = `${icon} ${prio}${agent.name}${project} ${what.replace(/\*/g, '')}${intent}${link ? `\nOpen: ${link}` : ''}`;
274
274
  sendSlack(text, n, plain)
275
275
  .then(() => this.record(kind, agent, null, urgency))
package/src/plugins.js CHANGED
@@ -1,4 +1,4 @@
1
- // Plugins: how a separate package extends Agent Manager without changing it. A plugin is an
1
+ // Plugins: how a separate package extends AI Agent Manager without changing it. A plugin is an
2
2
  // ES module listed in config.json "plugins" (the daemon) or AGENTMGR_HUB_PLUGINS (the hub),
3
3
  // whose default export (or `register`) receives the API below:
4
4
  //
package/src/preview.js CHANGED
@@ -4,7 +4,7 @@
4
4
  // On this computer the tab loads the URL directly. From another device each site gets its
5
5
  // own HTTPS listener (same certificate and sign-in as remote access) that relays everything
6
6
  // to it at the root path, so apps that use absolute paths, redirects and WebSockets (hot
7
- // reload) keep working. This daemon's own ports are never relayed, and the Agent Manager
7
+ // reload) keep working. This daemon's own ports are never relayed, and the AI Agent Manager
8
8
  // session cookie is stripped before forwarding.
9
9
  import http from 'node:http';
10
10
  import https from 'node:https';
@@ -170,7 +170,7 @@ export class Previews {
170
170
  const tport = targetPort(url);
171
171
  const own = new Set([...this.reserved(), ...[...this.listeners.values()].map((l) => l.port)]);
172
172
  const self = isLoopback(url) || this.ownHosts().has(url.hostname.replace(/^\[|\]$/g, '').toLowerCase());
173
- if (self && own.has(tport)) throw Object.assign(new Error("That is Agent Manager's own port"), { status: 400 });
173
+ if (self && own.has(tport)) throw Object.assign(new Error("That is AI Agent Manager's own port"), { status: 400 });
174
174
  const key = `${url.protocol}//${url.hostname}:${tport}`;
175
175
  const existing = this.listeners.get(key);
176
176
  if (existing) return existing.port;
@@ -223,13 +223,13 @@ export class Previews {
223
223
  const cookies = req.headers.cookie ?? '';
224
224
  const until = this.okCookies.get(cookies);
225
225
  if (until && until > Date.now()) return null;
226
- if (!this.signedIn(cookies)) return [401, 'Sign in to Agent Manager on this machine first, then open this tab again.'];
226
+ if (!this.signedIn(cookies)) return [401, 'Sign in to AI Agent Manager on this machine first, then open this tab again.'];
227
227
  if (this.okCookies.size > 100) this.okCookies.clear();
228
228
  this.okCookies.set(cookies, Date.now() + 10_000);
229
229
  return null;
230
230
  }
231
231
 
232
- /** Request headers as the local server expects them: its own Host and Origin, no Agent Manager cookie. */
232
+ /** Request headers as the local server expects them: its own Host and Origin, no AI Agent Manager cookie. */
233
233
  forwardHeaders(entry, req, upgrade) {
234
234
  const { target } = entry;
235
235
  const self = `${target.protocol}//${target.label}`;
package/src/server.js CHANGED
@@ -18,6 +18,7 @@ import { Previews, parseUrl } from './preview.js';
18
18
  import { LiveBrowser } from './livebrowser.js';
19
19
  import * as remote from './remote.js';
20
20
  import { Store } from './store.js';
21
+ import { about, latestRelease } from './about.js';
21
22
 
22
23
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
23
24
  const require = createRequire(import.meta.url);
@@ -337,7 +338,7 @@ export async function startServer({ port = PORT, host = HOST } = {}) {
337
338
  api.post(
338
339
  '/notify/test',
339
340
  wrap(async () => {
340
- await sendSlack(':wave: Test from Agent Manager: alerts will arrive here when an agent needs you.', loadNotify());
341
+ await sendSlack(':wave: Test from AI Agent Manager: alerts will arrive here when an agent needs you.', loadNotify());
341
342
  return { ok: true };
342
343
  }),
343
344
  );
@@ -370,6 +371,8 @@ export async function startServer({ port = PORT, host = HOST } = {}) {
370
371
  api.post('/agents/:id/waiting/dismiss', wrap((req) => manager.dismissWaiting(req.params.id)));
371
372
  api.post('/agents/:id/review', wrap((req) => manager.requestReview(req.params.id, { force: true })));
372
373
  api.post('/agents/:id/review/ack', wrap((req) => manager.ackReview(req.params.id, req.body?.action)));
374
+ api.get('/about', wrap(() => about({ port, config: { home: paths.home, claudeBin: config.claudeBin } })));
375
+ api.get('/about/latest', wrap(() => latestRelease()));
373
376
  api.get('/judge', wrap(() => manager.judge.status()));
374
377
  // On or off for this daemon, kept in config.json. The judge spends calls on your own engine plan.
375
378
  api.put('/judge', wrap((req) => {
package/src/service.js CHANGED
@@ -75,7 +75,7 @@ export function unit() {
75
75
  const env = Object.entries(passEnv()).map(([k, v]) => `Environment=${k}=${v}\n`).join('');
76
76
  const cmd = command().map((a) => `"${a.replace(/(["\\])/g, '\\$1')}"`).join(' ');
77
77
  return `[Unit]
78
- Description=Agent Manager daemon (background Claude Code agents)
78
+ Description=AI Agent Manager daemon (background Claude Code agents)
79
79
 
80
80
  [Service]
81
81
  ExecStart=${cmd}
package/src/sessions.js CHANGED
@@ -1,4 +1,4 @@
1
- // Claude Code sessions started outside Agent Manager (a plain `claude` in a terminal), so they
1
+ // Claude Code sessions started outside AI Agent Manager (a plain `claude` in a terminal), so they
2
2
  // can be continued here. Claude Code keeps each session as ~/.claude/projects/<cwd>/<id>.jsonl;
3
3
  // the start and end of each file say where it ran, its title, branch and first ask.
4
4
  import { execFileSync } from 'node:child_process';
@@ -85,7 +85,7 @@ export function runningClaude() {
85
85
 
86
86
  /**
87
87
  * Interactive Claude Code sessions from the last `days`, newest first, leaving out `known`
88
- * session IDs (Agent Manager's own) and anything run under `excludeDir` (its one-off calls and
88
+ * session IDs (AI Agent Manager's own) and anything run under `excludeDir` (its one-off calls and
89
89
  * workspaces). inUse: "open" when a running claude names the session, "maybe" when one runs in
90
90
  * its folder and the session was active in the last 10 minutes.
91
91
  */
package/src/store.js CHANGED
@@ -12,7 +12,7 @@ const MIGRATIONS = {};
12
12
  * so a crash mid-write never loses the session IDs.
13
13
  *
14
14
  * Upgrades: older files are copied to `<file>.v<N>.bak` and migrated one version at a time.
15
- * A file from a newer Agent Manager is refused rather than overwritten (downgrades would lose
15
+ * A file from a newer AI Agent Manager is refused rather than overwritten (downgrades would lose
16
16
  * fields). The file as it was at startup is kept as `<file>.bak`.
17
17
  */
18
18
  export class Store {
@@ -38,7 +38,7 @@ export class Store {
38
38
  }
39
39
  const version = Number(parsed.version ?? 1);
40
40
  if (version > STATE_VERSION) {
41
- throw new Error(`${this.file} was written by a newer Agent Manager (state version ${version}; this one reads up to ${STATE_VERSION}). Upgrade Agent Manager, or restore a backup.`);
41
+ throw new Error(`${this.file} was written by a newer AI Agent Manager (state version ${version}; this one reads up to ${STATE_VERSION}). Upgrade AI Agent Manager, or restore a backup.`);
42
42
  }
43
43
  if (version < STATE_VERSION) {
44
44
  fs.copyFileSync(this.file, `${this.file}.v${version}.bak`);