agentus 0.0.0-stage → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +202 -0
- package/NOTICE +11 -0
- package/README.md +476 -2
- package/README.zh-CN.md +396 -0
- package/bin/agentus.mjs +147 -0
- package/docs/android-notify-contract.md +658 -0
- package/docs/dev/01-dev-loop.md +186 -0
- package/docs/dev/02-verify.md +237 -0
- package/docs/dev/03-release.md +218 -0
- package/docs/dev/04-recover.md +199 -0
- package/docs/dev/05-agent-driven.md +114 -0
- package/docs/dev/README.md +65 -0
- package/docs/installation.md +366 -0
- package/docs/installation.zh-CN.md +350 -0
- package/docs/refs/aionui-acp-rules.md +46 -0
- package/docs/refs/hermes-studio-ui-ideas.md +191 -0
- package/docs/transcript-folding.md +139 -0
- package/package.json +76 -5
- package/packages/server/mcp/plan-server.mjs +217 -0
- package/packages/server/mock/agent.mjs +693 -0
- package/packages/server/package.json +22 -0
- package/packages/server/src/acp/backends.ts +196 -0
- package/packages/server/src/acp/errors.ts +46 -0
- package/packages/server/src/acp/registry.ts +579 -0
- package/packages/server/src/acp/session-manager.ts +1868 -0
- package/packages/server/src/auth.ts +688 -0
- package/packages/server/src/dashscope.ts +235 -0
- package/packages/server/src/fs.ts +379 -0
- package/packages/server/src/hotwords.ts +132 -0
- package/packages/server/src/index.ts +1486 -0
- package/packages/server/src/notify/center.ts +994 -0
- package/packages/server/src/notify/types.ts +222 -0
- package/packages/server/src/plan/plan.ts +126 -0
- package/packages/server/src/pty-helper.py +135 -0
- package/packages/server/src/settings.ts +469 -0
- package/packages/server/src/store/export.ts +209 -0
- package/packages/server/src/store/store.ts +1238 -0
- package/packages/server/src/term.ts +201 -0
- package/packages/server/src/title.ts +109 -0
- package/packages/server/src/voice.ts +251 -0
- package/packages/shared/package.json +14 -0
- package/packages/shared/src/index.ts +271 -0
- package/packages/web/dist/assets/index-D667PX2c.css +32 -0
- package/packages/web/dist/assets/index-d9NzwPFu.js +283 -0
- package/packages/web/dist/coding-agents/hermes.png +0 -0
- package/packages/web/dist/coding-agents/qoder.svg +5 -0
- package/packages/web/dist/icons/agentus-icon-inverse.svg +301 -0
- package/packages/web/dist/icons/agentus-icon.svg +301 -0
- package/packages/web/dist/icons/agentus-mark-inverse.svg +301 -0
- package/packages/web/dist/icons/agentus-mark.svg +301 -0
- package/packages/web/dist/icons/apple-touch-icon-180.png +0 -0
- package/packages/web/dist/icons/favicon.ico +0 -0
- package/packages/web/dist/icons/icon-192.png +0 -0
- package/packages/web/dist/icons/icon-512.png +0 -0
- package/packages/web/dist/icons/icon-maskable-512.png +0 -0
- package/packages/web/dist/index.html +30 -0
- package/packages/web/dist/manifest.webmanifest +17 -0
- package/packages/web/dist/sw.js +61 -0
- package/scripts/make-cert.sh +101 -0
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
# Recovering when it breaks
|
|
2
|
+
|
|
3
|
+
This doc answers: the instance is broken or misbehaving — where do I look first, and how do
|
|
4
|
+
I fix it without making the box worse?
|
|
5
|
+
|
|
6
|
+
## Read the boot log first
|
|
7
|
+
|
|
8
|
+
The boot log is the authoritative account of what the server resolved. It prints **which
|
|
9
|
+
data directory won and why**, the auth state, and the TLS listener:
|
|
10
|
+
|
|
11
|
+
```
|
|
12
|
+
data dir: /Users/…/packages/server/.data (AGENTUS_DATA was set) ← the dir + the reason
|
|
13
|
+
auth on: user "admin"
|
|
14
|
+
https://0.0.0.0:8443 (self-signed …) ← present = TLS listener up
|
|
15
|
+
tls off: no cert at <DATA_DIR>/tls/cert.pem ← absent/off = no TLS
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
If the directory it names is not the one you expected, stop here — a server serving the
|
|
19
|
+
wrong store looks healthy (200s, valid `/api/version`) while the operator's session list is
|
|
20
|
+
empty. Triage the identity, not the liveness (see "Is it serving the wrong store?" below).
|
|
21
|
+
|
|
22
|
+
## Ports
|
|
23
|
+
|
|
24
|
+
| port | who |
|
|
25
|
+
|---|---|
|
|
26
|
+
| `8787` | live HTTP |
|
|
27
|
+
| `8443` | live TLS (public tunnel points here) |
|
|
28
|
+
| `8901` | dev / QA instance |
|
|
29
|
+
| `8648` | **Hermes Studio** — not Agentus, do not touch |
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
lsof -nP -iTCP:8787 -sTCP:LISTEN # who owns the port (returns the CHILD, not the supervisor)
|
|
33
|
+
ps -o pid,ppid,pgid,command -p <pid> # its process group and command line
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## "Address already in use"
|
|
37
|
+
|
|
38
|
+
`EADDRINUSE` means another process holds the port — often a previous instance that never
|
|
39
|
+
died, or (this box) an ambient `PORT=8648` pointed at Studio. The Agentus server reads
|
|
40
|
+
`AGENTUS_PORT`, never bare `PORT`, so a script that reads the ambient value will fight for
|
|
41
|
+
Studio's port. Do not just relaunch: identify the current holder, decide whether it is the
|
|
42
|
+
one you want, then stop it through its own launcher.
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
# clean stop of the dev instance (kills the process GROUP on :8901)
|
|
46
|
+
bash scripts/dev.sh stop
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
For live, stop it only via the release path ([03-release.md](./03-release.md)) — a bare kill
|
|
50
|
+
of the live listener ends the operator's sessions.
|
|
51
|
+
|
|
52
|
+
## Orphaned agent and shell child processes
|
|
53
|
+
|
|
54
|
+
Every agent child is spawned detached and its pid recorded, and the server reaps orphans on
|
|
55
|
+
next boot — so a crashed server's leftovers are cleaned up automatically. Shell sessions
|
|
56
|
+
(the workspace terminal) are killed with their socket. Problems appear when a **supervisor**
|
|
57
|
+
(the `tsx watch` wrapper) survives a crash: the listener it spawned may be gone while the
|
|
58
|
+
supervisor sits in a restart loop, or a leaked listener keeps holding the port.
|
|
59
|
+
|
|
60
|
+
**Never kill by a substring match.** A cleanup filtered on `packages/server/src/index.ts`
|
|
61
|
+
once ended 249 processes in one shot — the operator's own `hermes-studio` checkout has a
|
|
62
|
+
file at that same path, and its server was the only way into the machine. Rules:
|
|
63
|
+
|
|
64
|
+
1. **Enumerate first and print the full command line and cwd** of every candidate:
|
|
65
|
+
```bash
|
|
66
|
+
ps -eo pid,ppid,pgid,command | grep <pattern>
|
|
67
|
+
lsof -a -p <pid> -d cwd -Fn # where it is actually running
|
|
68
|
+
```
|
|
69
|
+
2. **Keep the list inside this worktree's path.** The operator's services are never in
|
|
70
|
+
scope. A `ps | grep` list also contains your own tool shell — exclude the current pid and
|
|
71
|
+
its ancestors, or an unfiltered kill ends your own command mid-flight.
|
|
72
|
+
3. **Prove the target is its own process group, then kill the group:**
|
|
73
|
+
```bash
|
|
74
|
+
ps -o pid,pgid,sess -p <pid>
|
|
75
|
+
kill -9 -- -<pgid> # negative pid = the group
|
|
76
|
+
```
|
|
77
|
+
Under a supervisor the LISTEN pid is the supervisor's child, whose pgid is the
|
|
78
|
+
supervisor's — killing only the child leaves the supervisor (which respawns and fights
|
|
79
|
+
for the port). This is why `dev.sh`/`relaunch.sh` kill `-$pgid` and, when `pgid != pid`,
|
|
80
|
+
the pid too.
|
|
81
|
+
4. **Fix the leak, not the puddle.** New scripts must use the shipped pattern —
|
|
82
|
+
`spawn(..., { detached: true })` and kill the process group — rather than cleaning up by
|
|
83
|
+
name afterwards.
|
|
84
|
+
5. **Re-check the neighbours afterwards:** one `curl` per resident service
|
|
85
|
+
(`:8648` Studio, `:8787`/`:8443` live, `:8901` dev). A cleanup that leaves the box worse
|
|
86
|
+
than it found it is the whole failure mode.
|
|
87
|
+
|
|
88
|
+
## The SQLite store, WAL, and the `*.bak` files
|
|
89
|
+
|
|
90
|
+
State lives in `<DATA_DIR>/agentus.sqlite` (the transcript and every other row), accessed
|
|
91
|
+
through Node's built-in `node:sqlite` (hence Node `>= 22.5`). Files beside it: `credentials.json`,
|
|
92
|
+
`auth.token`, `auth.secret`, `sessions.json`, `settings.json`, `tls/` — several of them
|
|
93
|
+
`0600`. The server creates the directory `0700` on first boot.
|
|
94
|
+
|
|
95
|
+
**Back up before you touch the store.** A single write against the operator's real data gets
|
|
96
|
+
a backup and a record of the old values:
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
cp -p packages/server/.data/agentus.sqlite \
|
|
100
|
+
~/.hermes/cache/scratch/agentus.sqlite.bak-$(date +%H%M%S)
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
The one migration path in the project that rewrites rows in place — the legacy transcript
|
|
104
|
+
fold — takes its own backup first with `VACUUM INTO <db>.pre-fold.bak` and gates itself on
|
|
105
|
+
`pragma user_version`, so it runs once and is idempotent. If you see a `*.pre-fold.bak`
|
|
106
|
+
beside the store, that fold ran; the original is inside it.
|
|
107
|
+
|
|
108
|
+
Two cautions that come from real incidents:
|
|
109
|
+
|
|
110
|
+
- **Two independent writers on one WAL database** corrupted the operator's live `state.db`
|
|
111
|
+
while the runtime linked a SQLite in the WAL-reset bug range. The fix is the interpreter
|
|
112
|
+
(link SQLite ≥ 3.51.3), not a symlink, and it belongs to the Hermes Studio runtime, not to
|
|
113
|
+
this repo. Check it with `hermes doctor` and read the ⚠ lines — a warning is real, not
|
|
114
|
+
imaginary, but the shipped `hermes doctor` is warn-only. Never add a second writer to the
|
|
115
|
+
live home on a vulnerable build.
|
|
116
|
+
- `DELETE /api/sessions/<id>` **closes a live session and purges a cold one** — for an
|
|
117
|
+
already-dead row it deletes the record. So "archive this dead session" is not that route;
|
|
118
|
+
it takes a direct, backed-up, single-row transaction (`WHERE id=? AND status='error'`), no
|
|
119
|
+
live row touched, and no restart needed (a cold row is not in the server's in-memory map).
|
|
120
|
+
|
|
121
|
+
## Is it serving the wrong store?
|
|
122
|
+
|
|
123
|
+
Symptoms that look like separate bugs but share one cause: the operator's session list came
|
|
124
|
+
back empty, TLS silently turned off (`tls off: no cert at <QA_DIR>/tls/cert.pem`), and a
|
|
125
|
+
running slot was reaped — yet the port answers 200 and `/api/version` looks fine. Cause:
|
|
126
|
+
`relaunch → launch.py` inherited the QA environment. The launcher now pins its own
|
|
127
|
+
`AGENTUS_DATA`/`AGENTUS_PORT` and pops the QA/DEV keys, but verify identity directly:
|
|
128
|
+
|
|
129
|
+
```bash
|
|
130
|
+
PID=$(lsof -nP -iTCP:8787 -sTCP:LISTEN -t)
|
|
131
|
+
ps eww -p "$PID" | tr ' ' '\n' | grep AGENTUS # the process's own identity vars
|
|
132
|
+
lsof -p "$PID" | grep sqlite # which store it actually opened
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
Recovery:
|
|
136
|
+
|
|
137
|
+
```bash
|
|
138
|
+
cd worktrees/agentus
|
|
139
|
+
env -u AGENTUS_DATA -u AGENTUS_BASE -u DEV_LOG_PATH -u DEV_PROMOTE_DETACHED -u DEV_PROMOTE_SELF \
|
|
140
|
+
bash scripts/relaunch.sh --port 8787 --launcher ~/.hermes/cache/agentus/launch.py
|
|
141
|
+
TOK=$(cat packages/server/.data/auth.token)
|
|
142
|
+
curl -s -X POST -H "Authorization: Bearer $TOK" http://127.0.0.1:8787/api/sessions/<id>/resume
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
After any restart, put the operator back on the air: `POST /api/sessions/<id>/resume` with the
|
|
146
|
+
machine token → `status: ready` and a fresh pid. **Capture the slot ids to resume before the
|
|
147
|
+
restart** — in the window right after a restart a slot has dropped from `live` into
|
|
148
|
+
`archived` wearing `error`, so a resume loop that reads the current `live` list finds
|
|
149
|
+
nothing. `resume` works on an `archived('error')` row; that is the cold-slot entry point.
|
|
150
|
+
|
|
151
|
+
## Auth lockout
|
|
152
|
+
|
|
153
|
+
The login limiter is visible and reversible without a restart, so locking yourself out is a
|
|
154
|
+
one-click problem. Endpoints behind the login:
|
|
155
|
+
|
|
156
|
+
```bash
|
|
157
|
+
TOK=$(cat <DATA_DIR>/auth.token)
|
|
158
|
+
curl -s -H "Authorization: Bearer $TOK" http://127.0.0.1:8787/api/auth/locked-ips
|
|
159
|
+
curl -s -X DELETE -H "Authorization: Bearer $TOK" http://127.0.0.1:8787/api/auth/locked-ips
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
In the UI it is Settings → 登录失败锁定 (which IPs are locked, for how long, unlock / unlock
|
|
163
|
+
all). Tunables: `AGENTUS_LOGIN_MAX_FAILS` (default 5) / `AGENTUS_LOGIN_LOCK_MS` (default
|
|
164
|
+
30000). Login sessions are stateless signed cookies indexed at `<DATA_DIR>/sessions.json`
|
|
165
|
+
(0600) — advisory for display, authoritative for existence. A password change invalidates
|
|
166
|
+
every other session; `AGENTUS_AUTH=off` disables the login entirely and the boot log scolds
|
|
167
|
+
you (do not leave it off — the listener is `0.0.0.0` and this cockpit is tunnelled).
|
|
168
|
+
|
|
169
|
+
## TLS certificate regeneration
|
|
170
|
+
|
|
171
|
+
The TLS listener runs on a **second port** (8443 live), pointed at by a plain-TCP tunnel.
|
|
172
|
+
Cert material lives at `<DATA_DIR>/tls/`. Regenerate with:
|
|
173
|
+
|
|
174
|
+
```bash
|
|
175
|
+
scripts/make-cert.sh [name] # default name: the DDNS name; SAN = that name + localhost + 127.0.0.1
|
|
176
|
+
scripts/make-cert.sh --leaf-only # rotate the leaf only; installed devices are untouched
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
`make-cert.sh` issues **two** certs on purpose: a 10-year root (`ca.pem`, what devices install
|
|
180
|
+
once, served at `/cert.crt`) and a 390-day leaf (`cert.pem`/`key.pem`, what the listener
|
|
181
|
+
serves, under Apple's 398-day cap). Keep the SAN to the DDNS name you actually type, not the
|
|
182
|
+
dynamic public IP. It writes into the same directory the server reads from (mirroring the
|
|
183
|
+
server's own resolution in `packages/server/src/index.ts`), and if that resolution and the
|
|
184
|
+
server's ever diverge the listener stays silently off with a cert sitting next to it — after
|
|
185
|
+
regenerating, confirm the boot log still prints `https://…:8443`.
|
|
186
|
+
|
|
187
|
+
## The process-exit safety net for children
|
|
188
|
+
|
|
189
|
+
The server installs `SIGINT`/`SIGTERM`/`SIGHUP` handlers that terminate the child agent
|
|
190
|
+
processes, so Ctrl-C (or a deliberate restart) takes its agents with it. The
|
|
191
|
+
`unhandledRejection`/`uncaughtException` guards are installed **only after `listen()`
|
|
192
|
+
succeeds** — installing them earlier would turn a startup failure (`EADDRINUSE`) into a
|
|
193
|
+
zombie that is "alive but not listening". Combined with the on-boot orphan reap, this is
|
|
194
|
+
what keeps a crashed server from leaving runaway children behind. If you find runaway
|
|
195
|
+
children anyway, hunt them by the process-group rule above — never by name.
|
|
196
|
+
|
|
197
|
+
Cross-links: [01-dev-loop.md](./01-dev-loop.md) (ports, data dirs) ·
|
|
198
|
+
[03-release.md](./03-release.md) (release + rollback) ·
|
|
199
|
+
[02-verify.md](./02-verify.md) (stray CDP tabs, leaked servers).
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
# Developing Agentus with a coding agent in the loop
|
|
2
|
+
|
|
3
|
+
This doc answers: how do I hand a task on this repository to a coding agent, and how do I
|
|
4
|
+
review what it produces?
|
|
5
|
+
|
|
6
|
+
## AGENTS.md — the file an agent reads first
|
|
7
|
+
|
|
8
|
+
`AGENTS.md` is a plain-markdown conventions file that coding agents (Hermes, Qoder CLI and
|
|
9
|
+
others) read automatically at the start of a session. It is where a repo states the things
|
|
10
|
+
that are true of every change: where things live, the invariants, the git discipline, the
|
|
11
|
+
commands to run. It is not documentation for humans — it is the brief an agent gets before
|
|
12
|
+
it touches anything.
|
|
13
|
+
|
|
14
|
+
**The Agentus repository itself carries no `AGENTS.md` as of this writing** — the
|
|
15
|
+
conventions that govern work on it live in this `docs/dev/` set plus the operator's own
|
|
16
|
+
working notes. `TODO(verify)`: a `docs/dev/`-aware root `AGENTS.md` may be added; if one
|
|
17
|
+
appears it becomes the entry point an agent reads first, and this manual is what it links to.
|
|
18
|
+
|
|
19
|
+
Where `AGENTS.md` **is** load-bearing in this workspace: the operator's spoken-reply
|
|
20
|
+
workspace (`~/Workspace/voice-chat/AGENTS.md`) is read by both `hermes` and `qodercli`, so
|
|
21
|
+
the same style rules apply whichever backend a session runs on. A running session keeps the
|
|
22
|
+
prompt it booted with, so an edit to that file only shows up in a **new** session. When a
|
|
23
|
+
task is about how Agentus-backed sessions should *speak*, that file — not the code — is the
|
|
24
|
+
lever.
|
|
25
|
+
|
|
26
|
+
## Handing a task to an agent
|
|
27
|
+
|
|
28
|
+
Agentus is a good target for agent work because every claim is checkable in-repo. Give the
|
|
29
|
+
agent four things and it can work without guessing.
|
|
30
|
+
|
|
31
|
+
**1. The real entry points.** Name the files, do not describe them:
|
|
32
|
+
|
|
33
|
+
| area | file |
|
|
34
|
+
|---|---|
|
|
35
|
+
| server entry | `packages/server/src/index.ts` (boots, resolves data dir, wires routes/WS) |
|
|
36
|
+
| spawn + ACP relay | `packages/server/src/acp/registry.ts`, `acp/backends.ts` |
|
|
37
|
+
| store | `packages/server/src/store/store.ts` |
|
|
38
|
+
| auth | `packages/server/src/auth.ts` |
|
|
39
|
+
| web app | `packages/web/src/App.tsx`, `state.ts`, `transcript.ts` |
|
|
40
|
+
| mock agent (dev/QA) | `packages/server/mock/agent.mjs` |
|
|
41
|
+
| dev launcher | `scripts/dev.sh` · release: `scripts/relaunch.sh` · `scripts/make-cert.sh` |
|
|
42
|
+
|
|
43
|
+
**2. The reference implementation to compare against.** Almost every UI question has a
|
|
44
|
+
sibling project that already answered it, and the operator expects behaviour parity, not
|
|
45
|
+
copied markup. Point the agent at the right checkout (below) and ask for the *contract* —
|
|
46
|
+
where the control lives, what it reads, how it behaves — never the markup.
|
|
47
|
+
|
|
48
|
+
**3. The verification the change must run.** State it up front so the agent does not declare
|
|
49
|
+
victory on a build:
|
|
50
|
+
|
|
51
|
+
- `npm run typecheck` (0 errors), then `npm run build`;
|
|
52
|
+
- the server smokes for the area (`node scripts/*.mjs`);
|
|
53
|
+
- for a UI change, a CDP sweep **and** `npm run build` before it (the server serves the built
|
|
54
|
+
bundle). If no sweep covers the surface, the round writes one — see [02-verify.md](./02-verify.md);
|
|
55
|
+
- for a release, the deployed-bundle checks in [03-release.md](./03-release.md).
|
|
56
|
+
|
|
57
|
+
**4. The boundary.** The live instance may be restarted only by an explicit release, and
|
|
58
|
+
only on the operator's word unless the change is strictly web-only. Say this in the brief;
|
|
59
|
+
an unprompted `promote` kills the session the agent is running in.
|
|
60
|
+
|
|
61
|
+
## Reference implementations, and where they live
|
|
62
|
+
|
|
63
|
+
The operator asks to align a surface with these two products ("体验对齐" = behaviour parity).
|
|
64
|
+
Read the checkouts before designing; take the contract, not the code — a genuinely derived
|
|
65
|
+
block needs its license/NOTICE line, an idea needs nothing.
|
|
66
|
+
|
|
67
|
+
| product | checkout | what it is good for |
|
|
68
|
+
|---|---|---|
|
|
69
|
+
| AionUi | `~/Project/AionUi` (`packages/desktop/src/renderer`) | layout mechanics: resizable panes, grouped history, local-file preview, table zoom, markdown utils; its `docs/prds/**` give per-surface intent *and* a "已知局限" appendix (the admitted flaws are what NOT to copy) |
|
|
70
|
+
| hermes-studio | `~/.hermes/cache/studio-ref` (client) | the session menu, tool-run grouping (`ToolRunSummary.vue`, `tool-run-grouping.ts`), the message queue, `appendedTextDelta` streaming |
|
|
71
|
+
|
|
72
|
+
Repo-side notes on both are under [`docs/refs/`](../refs/) (`aionui-acp-rules.md`,
|
|
73
|
+
`hermes-studio-ui-ideas.md`).
|
|
74
|
+
|
|
75
|
+
Two rules that come out of using them:
|
|
76
|
+
|
|
77
|
+
- **Code, not screenshots.** A reference's source gives the decisions (e.g. "running tools
|
|
78
|
+
are not grouped"; the exact `useResizableSplit` clamping) that a screenshot hides.
|
|
79
|
+
- **Check the form before borrowing.** AionUi is an Electron app; a mechanism that hangs off
|
|
80
|
+
its Titlebar has no equivalent in a pure web page + Android WebView shell. Ask "does it
|
|
81
|
+
attach to a layer we have?" before copying it.
|
|
82
|
+
|
|
83
|
+
## Review discipline
|
|
84
|
+
|
|
85
|
+
These are the habits that keep agent-produced changes safe on a two-instance, live-traffic
|
|
86
|
+
codebase.
|
|
87
|
+
|
|
88
|
+
- **Diagnose before editing.** For a reported defect or a "why did it do that" question,
|
|
89
|
+
produce the mechanism (store query, log line, code path) and the numbers **first**, and
|
|
90
|
+
stop there. A diff in the same turn as a question is answering a question with
|
|
91
|
+
unrequested work. When a spec is restated, write your reading back — the shape, one
|
|
92
|
+
example, and every point where your reading could differ — before touching code.
|
|
93
|
+
- **Root cause over symptom, and fix the class.** The operator reports the symptom
|
|
94
|
+
accurately and the cause never. Find the general condition, not just the instance the
|
|
95
|
+
report names, and say which class of defect it belongs to.
|
|
96
|
+
- **One logical change per commit.** Write the message to a file and use `-F` (inline
|
|
97
|
+
backticks / `$` / `!` are eaten by the shell). If a file carries someone else's in-flight
|
|
98
|
+
work, commit only your hunk (`git diff -U3 -- <file>` → `git apply --cached <patch>`),
|
|
99
|
+
never a plain `git add <file>`.
|
|
100
|
+
- **Reproduce before you fix; make the new check prove the bug.** Run the assertion against
|
|
101
|
+
the current (broken) build, quote the failing count, then fix, then re-run. A check that
|
|
102
|
+
never failed on the broken build proves nothing.
|
|
103
|
+
- **Measure before and after, and put the numbers in the reply.** "Looks the same to me" is
|
|
104
|
+
not evidence, and a light/dark screenshot pair can be two copies of one theme — compare
|
|
105
|
+
real values.
|
|
106
|
+
- **Re-verify the checks that watched the thing you changed.** A behaviour change
|
|
107
|
+
invalidates its own observation point; a harness edit invalidates every green it produced.
|
|
108
|
+
- **Leave the cockpit as you found it.** Purge the sessions, workspaces and probe files the
|
|
109
|
+
round created; keep the operator's; report the final counts.
|
|
110
|
+
- **`main` and the live instance move only on their own paths** — merge/release per
|
|
111
|
+
[03-release.md](./03-release.md), never as a side effect of a "fix".
|
|
112
|
+
|
|
113
|
+
Cross-links: [01-dev-loop.md](./01-dev-loop.md) · [02-verify.md](./02-verify.md) ·
|
|
114
|
+
[03-release.md](./03-release.md) · [04-recover.md](./04-recover.md).
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# Developing Agentus
|
|
2
|
+
|
|
3
|
+
This is the in-repo manual for working on Agentus: how to run a development instance,
|
|
4
|
+
how to prove a change works, how to release it to the live instance, and how to recover
|
|
5
|
+
when it breaks.
|
|
6
|
+
|
|
7
|
+
**What Agentus is.** A multi-session web cockpit for ACP-speaking coding agents
|
|
8
|
+
(`hermes acp` / `qodercli --acp`). The server spawns one child process per session and
|
|
9
|
+
relays ACP JSON-RPC between the browser and that agent. The UI holds no intelligence:
|
|
10
|
+
gate access → spawn → relay → stream → persist → surface permission cards. State lives in
|
|
11
|
+
`<DATA_DIR>` (default `~/.agentus`; see [01-dev-loop.md](./01-dev-loop.md) for how the
|
|
12
|
+
directory resolves). Node `>= 22.5` is required — the store is `node:sqlite`.
|
|
13
|
+
|
|
14
|
+
## The mental model
|
|
15
|
+
|
|
16
|
+
There are **two instances of the same codebase**, distinguished only by environment
|
|
17
|
+
variables, and they must never touch each other's code or data:
|
|
18
|
+
|
|
19
|
+
| | live | dev |
|
|
20
|
+
|---|---|---|
|
|
21
|
+
| tree | `worktrees/agentus` | `worktrees/agentus-dev` |
|
|
22
|
+
| role | the instance the operator actually uses | where changes are made and tested |
|
|
23
|
+
| HTTP port | `8787` | `8901` |
|
|
24
|
+
| TLS port | `8443` | off (`AGENTUS_TLS_PORT=0`) |
|
|
25
|
+
| data dir | `<tree>/packages/server/.data` (pinned by the launcher) | `/tmp/agentus-qa-account` |
|
|
26
|
+
| launcher | `~/.hermes/cache/agentus/launch.py` | `scripts/dev.sh start\|stop\|status\|logs` |
|
|
27
|
+
| hot reload | **no** — restart is explicit | yes (`tsx watch`) |
|
|
28
|
+
|
|
29
|
+
**Why two trees.** `tsx watch` reloads the whole server on any server-source edit, and a
|
|
30
|
+
reload SIGTERMs every child process — every live `hermes acp` session, including the one
|
|
31
|
+
the operator is typing into. Editing server code in the live tree would therefore kill his
|
|
32
|
+
conversation mid-turn (measured: page went white mid-turn, the turn died). The live tree
|
|
33
|
+
runs **without** a watcher and is only moved by an explicit release
|
|
34
|
+
([03-release.md](./03-release.md)). All edits happen in the dev tree.
|
|
35
|
+
|
|
36
|
+
**The loop you repeat every round:**
|
|
37
|
+
|
|
38
|
+
```
|
|
39
|
+
change (dev tree) → verify (sweeps + suites) → promote (dev→live) → record (track + lessons)
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Nothing merges to `main` or reaches the live instance without the operator saying so —
|
|
43
|
+
the one exception is a strictly web-only change, and even that is bounded by what
|
|
44
|
+
`promote` prints (see [03-release.md](./03-release.md)).
|
|
45
|
+
|
|
46
|
+
## The five documents
|
|
47
|
+
|
|
48
|
+
| doc | the question it answers |
|
|
49
|
+
|---|---|
|
|
50
|
+
| [01-dev-loop.md](./01-dev-loop.md) | How do I run a development instance, and how is its data kept separate from live? |
|
|
51
|
+
| [02-verify.md](./02-verify.md) | How do I prove a change actually works — unit suites and real-browser sweeps? |
|
|
52
|
+
| [03-release.md](./03-release.md) | How do I get a verified change onto the live instance, and how do I roll it back? |
|
|
53
|
+
| [04-recover.md](./04-recover.md) | The instance is broken — where do I look, and how do I fix it safely? |
|
|
54
|
+
| [05-agent-driven.md](./05-agent-driven.md) | How do I hand a task on this repo to a coding agent, and review what it does? |
|
|
55
|
+
|
|
56
|
+
## Related in-repo docs
|
|
57
|
+
|
|
58
|
+
- [`docs/transcript-folding.md`](../transcript-folding.md) — how one reply becomes rows,
|
|
59
|
+
pages and bubbles (the store's row semantics).
|
|
60
|
+
- [`docs/android-notify-contract.md`](../android-notify-contract.md) — the server↔phone
|
|
61
|
+
notification contract.
|
|
62
|
+
- [`docs/refs/`](../refs/) — notes on the reference UIs and ACP rules this project borrows
|
|
63
|
+
behaviour from.
|
|
64
|
+
- [`README.md`](../../README.md) — install, environment variables, login, TLS and the
|
|
65
|
+
public tunnel.
|