@primusclaw/connector 0.1.4 → 0.1.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +155 -38
  2. package/dist/cli.js +2668 -457
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -41,8 +41,12 @@ If npm answers `EACCES`, point its global prefix at a directory you own (`npm co
41
41
  ```sh
42
42
  claw-connector init # writes agents.toml and prints where: ~/.config/claw-connector/agents.toml unless XDG_CONFIG_HOME or CLAW_CONNECTOR_HOME is set
43
43
  claw-connector status # after you edit it: prints a "problem:" line if the file is wrong
44
+ claw-connector doctor # checks the machine (step 5), then starts each agent's adapter, opens a session (no prompt) and checks the modes it offers
44
45
  ```
45
46
 
47
+ `init` runs the adapter check of `doctor` at the end. `doctor` says `ok` for an agent whose phone sessions will open, and otherwise a
48
+ `problem:` line with the fix, for example an agent whose sessions would be refused with `mode_unavailable`.
49
+
46
50
  By default every agent asks before it writes a file or runs a command, and your phone answers each question. To let phone sessions run
47
51
  like your terminal instead, run `claw-connector init --approval never` (or set `approval = "never"` on an agent later); see
48
52
  [Running without per-step approval](#running-without-per-step-approval).
@@ -80,15 +84,36 @@ claw-connector service status
80
84
 
81
85
  This starts the connector now and keeps it up: at login and after a reboot, and again 5 seconds after it dies for any reason, except
82
86
  one that only you can fix (the connector was revoked, or Claw refused the sign-in), which leaves it stopped until you run `login` again.
87
+ Only `claw-connector stop` (or stopping the service) stops it for good: a `kill` of the connector's process from anywhere else ends it
88
+ with exit 75, and it is started again.
83
89
  It uses `systemd --user` on Linux and launchd on macOS. On Linux, run `loginctl enable-linger $USER` once (`service install` says so
84
90
  when it is needed) so it also runs while you are logged out. In a container without systemd, `service install` starts a small
85
- supervisor and writes `keepalive.sh`; have the container's startup command and a 5-minute cron job run that script (it does nothing
86
- when the supervisor is up) so a restarted container comes back by itself. See [Keeping it running](#keeping-it-running).
91
+ supervisor, writes `keepalive.sh` and adds it to your crontab when a cron daemon runs; when none does it says so, and the container's
92
+ startup command must run that script (it does nothing when the supervisor is up) for a restarted container to come back. Keys the
93
+ agents read from your shell (`ANTHROPIC_API_KEY`, `OPENAI_API_KEY`...) are kept for the service in a 0600 file, never in the unit.
94
+ See [Keeping it running](#keeping-it-running).
87
95
 
88
96
  To try it in the foreground instead, `claw-connector run` prints `connecting claude to https://claw.example.com/claw-api as
89
97
  "<machine name>"` and stays up until Ctrl-C, which stops it and every agent it started.
90
98
 
91
- **5. See it in Clawy**
99
+ **5. Check the machine**
100
+
101
+ ```sh
102
+ claw-connector doctor
103
+ ```
104
+
105
+ `doctor` checks what makes a first phone session fail far from the machine that caused it, and prints one fix per problem:
106
+ Node.js 22 or newer; each agent's program on `PATH` (and on the `PATH` the installed service starts it with, which is not your
107
+ shell's); whether Claude Code and Codex are signed in (`claude auth status`, `codex login status`, or the model provider's key
108
+ variable in `~/.codex/config.toml`); the `roots` writable; `HTTPS_PROXY` and `https_proxy` not set to different values; a service
109
+ installed (and, for `keepalive.sh`, something calling it); and Claw answering, including whether Node.js trusts its certificate
110
+ (Node does not read the system's CA store by default: behind a company CA set `NODE_USE_SYSTEM_CA=1` or `NODE_EXTRA_CA_CERTS`). Then it
111
+ runs the adapter check of step 2. It exits 1 when something will stop sessions (`--json`: `ok` is the adapters' verdict,
112
+ `machine_ok` and `environment` the machine's). The
113
+ running connector repeats the machine checks with its own environment (in a child process, so a stalled disk cannot stall
114
+ it) every 15 minutes, and at once when `doctor` runs (it signals the connector), and Clawy's `/agents` card shows what it found under the machine ("Codex is not signed in: run `codex login` on that machine").
115
+
116
+ **6. See it in Clawy**
92
117
 
93
118
  In Clawy (or any chat client built on Claw), send `/agents`. The agents of this machine are listed, online while `run` is up.
94
119
  A session started there runs on this machine, inside one of your `roots`, and each permission request comes back as a card in the chat.
@@ -116,7 +141,9 @@ id = "codex"
116
141
  preset = "codex"
117
142
  roots = ["~/work/repo-a", "~/work/repo-b"]
118
143
  [agents.env] # passed to the adapter; the model settings live here, never in Claw
119
- OPENAI_API_KEY = "..." # or leave your own environment / CODEX_HOME to supply it
144
+ OPENAI_API_KEY = "..." # or leave your own environment / CODEX_HOME to supply it; with only a key, the
145
+ # connector signs Codex in with it (Codex keeps it in its auth.json, like
146
+ # "codex login --with-api-key")
120
147
 
121
148
  [[agents]]
122
149
  id = "mine"
@@ -136,10 +163,23 @@ Presets and the adapter versions they pin (override `command` and `args` to move
136
163
  | `codex` | `npx @agentclientprotocol/codex-acp@2.3.0` | `read-only` | `read-only` | `agent-full-access` |
137
164
  | `cursor` | `agent acp` (the Cursor CLI) | `ask` | `ask`, `plan` | `agent`, started as `agent --force acp` |
138
165
 
166
+ The pinned adapters (the `npx -y <package>@<version>` presets above) are installed once into the connector's state
167
+ directory (`adapters/<package>@<version>`), by `init`, by `service install` and in the background when `run` starts, and are
168
+ then started from there with the connector's own Node.js: a session does not wait for npx to look the package up in a
169
+ registry (about 70 seconds when the registry cannot be reached) or download it (Claude's adapter takes about 350 MB on
170
+ disk with its dependencies), and a repository's own `.npmrc` cannot pick the registry. The install uses `npm install` with your npm
171
+ settings (`~/.npmrc`, `npm_config_*`, an `npm_config_registry` in the agent's `env`). A session that opens while its
172
+ adapter is still being installed waits for it; if the install failed, the agent's npx command runs as before, in the
173
+ connector's state directory, and the install is tried again in the background ten minutes later. An agent with its own
174
+ `command` or `args` is started exactly as written.
175
+
139
176
  `claude-attach` does not start new conversations: it takes over Claude Code conversations that already exist on this
140
177
  machine (see [Taking over an existing conversation](#taking-over-an-existing-conversation)).
141
178
 
142
- An agent with no `roots` is never offered. A configuration problem is reported at start-up, not skipped.
179
+ An agent with no `roots` is never offered. A configuration problem is reported at start-up, not skipped. A root that does
180
+ not exist, or whose disk does not answer (a network mount that is down or not mounted yet), is not a configuration problem:
181
+ `run` warns about it and keeps going, and that root's sessions are refused (`cwd_not_found`, `root_unavailable`) until it is
182
+ back. Reading `agents.toml` never touches a root.
143
183
 
144
184
  ## Running without per-step approval
145
185
 
@@ -165,23 +205,20 @@ unattended while Cursor keeps asking. For that agent the connector then:
165
205
  `mode_update` carry `unattended`, so a client can always show whether a session asks. `status` and the line `run` prints
166
206
  at start-up name the agents without per-step approval.
167
207
 
168
- **Before the first phone session, check which way your Claude settings go.** When `permissions.defaultMode` is
169
- `bypassPermissions` in your user `settings.json` (`$CLAUDE_CONFIG_DIR`, else `~/.claude`) or in Claude's managed settings,
170
- every session of a `claude` agent without `approval = "never"` is closed with `mode_not_enforced` (a `claude-attach`
171
- agent accepts the warning while it takes a conversation over, so it is not affected).
172
- `claw-connector status` and the start-up line of `run` say so for each such agent:
173
-
174
- ```
175
- warning: claude: sessions will be refused (mode_not_enforced): Claude defaults to bypassPermissions here (permissions.defaultMode in /home/me/.claude/settings.json); set approval = "never" on this agent or change defaultMode
176
- ```
177
-
178
- Choose one: `approval = "never"` on that agent (knowing the risk below), or another `defaultMode` in that file. The check
179
- reads the two files from which the Claude adapter takes `bypassPermissions` (it ignores that value in a repository's
180
- `.claude/settings.json` and `.claude/settings.local.json`), and only the exact spelling counts, as for the adapter; root
181
- without `IS_SANDBOX` cannot use bypass, and `permissions.disableBypassPermissionsMode = "disable"` turns it off. It applies
182
- to every agent that runs `claude-agent-acp` except `claude-attach`. Codex and Cursor have no equivalent: their own
208
+ **Your own Claude settings may default to `bypassPermissions`** (`permissions.defaultMode` in your user `settings.json`,
209
+ `$CLAUDE_CONFIG_DIR` or `~/.claude`, or in Claude's managed settings). For a `claude` agent that asks (no
210
+ `approval = "never"`), the connector tells the adapter to leave that mode out (`allowDangerouslySkipPermissions: false`),
211
+ so its sessions start in `default` and ask; before 0.1.5 each of them was closed with `mode_not_enforced`. An agent with
212
+ `approval = "never"` runs in `bypassPermissions` either way. A `claude-attach` agent takes over a conversation in the mode
213
+ its terminal chose and accepts the adapter's warning while it starts. Codex and Cursor have no equivalent: their own
183
214
  settings are replaced by the mode the connector sets and reads back.
184
215
 
216
+ **Root.** Claude Code offers `bypassPermissions` to root only when `IS_SANDBOX` is set. For an agent that runs
217
+ `claude-agent-acp` with `approval = "never"` (or `allow_unsafe_modes = true`), the connector sets `IS_SANDBOX=1` in that
218
+ agent's adapter environment when it runs as root, and `run` prints a `note:` line saying so; nothing else gets it. Without
219
+ that, every phone session of such an agent was refused with `mode_unavailable`. To keep it off, set `IS_SANDBOX` yourself
220
+ in the agent's `[agents.env]` (an empty value is kept as it is; `status` then warns that sessions will be refused).
221
+
185
222
  **The risk, in one sentence:** an agent without per-step approval acts on whatever it is told, including text it read in
186
223
  a file or a web page, with everything your account can do inside its roots (and `--force` or full access reach outside the
187
224
  sandbox the adapter would otherwise keep), and nobody is asked first.
@@ -209,7 +246,24 @@ All of these run on your machine and fail closed.
209
246
 
210
247
  - **Directories.** A session's working directory is resolved (symlinks included) and must be inside one of the agent's
211
248
  `roots`; otherwise no process is started and the session is closed with `cwd_outside_roots`. The agent runs in the
212
- resolved path.
249
+ resolved path. A folder can be given the way people type it: `~` and `~/x` are the agent's home directory (its
250
+ `[agents.env]` `HOME`, else the connector's), and a relative name (`repo`, `repo/sub`) is looked up under each root and
251
+ taken when exactly one has it (`cwd_ambiguous` when several do); a root's own last segment names that root. A relative
252
+ name never climbs out (`..`). Every look at the disk on the way to opening a session is asynchronous with a 10-second
253
+ limit, so a root on a stalled network mount refuses its own sessions (`root_unavailable`), never those under a healthy
254
+ root, and never freezes the connector or its heartbeat. A restored session whose root does not answer is kept: its
255
+ commands are rejected `root_unavailable` until it does, and `close` still closes it.
256
+ - **Signed in before ready.** A new session of an agent that reports its sign-in (Claude Code does) and reports none is
257
+ refused at once with `agent_auth_required`, instead of opening and failing at its first message; a session that
258
+ already exists is kept and its prompts say why. A prompt refused for the same reason later
259
+ (an expired login) ends its turn with `error: "agent_auth_required"` and the fix, and the next prompt starts the agent
260
+ afresh, so a sign-in done on the machine meanwhile is picked up. An adapter that needs a sign-in and offers an
261
+ `api-key` method (Codex) is told to `authenticate` with it when its environment has `OPENAI_API_KEY` or `CODEX_API_KEY`;
262
+ the key itself is never sent, the adapter reads its own environment.
263
+ - **Why, in one sentence.** Every refusal (`session_closed`, `command_rejected`, and a `turn_end` that failed for a known
264
+ reason) carries its stable `reason` code and a `hint`: one English sentence of what to do about it (sign in with which
265
+ command, which folder to pick, `claw-connector doctor`), with no path and nothing secret, for a client to show beside
266
+ its own translation of the code.
213
267
  - **Ask mode, read back.** After the adapter creates a session, the connector sets the agent's ask mode and reads the
214
268
  mode back from the adapter. Your own agent settings may start an adapter in a mode that never asks (bypass, full
215
269
  access); an adapter that does not confirm the ask mode, or that warns it will not invoke its permission callback, is
@@ -229,6 +283,9 @@ All of these run on your machine and fail closed.
229
283
  which cannot write. `approval = "never"` selects `agent` and starts `agent --force acp` (`--force` is a root flag of the
230
284
  Cursor CLI, "force allow commands unless explicitly denied"; it is accepted before or after `acp`). Cursor
231
285
  has no `session/resume`; the connector uses `session/load` and marks the replayed history `replay: true`.
286
+ A Cursor agent that asks therefore answers and plans but changes no file: its capabilities say `edits: false` with an
287
+ `edits_hint`, `read_only_modes` lists `ask` and `plan` (each such mode in `session_started` carries `read_only: true`),
288
+ `run` prints a note, and the sample `agents.toml` says so above the Cursor block.
232
289
  - **Permissions.** Each permission request becomes a card with the options **the adapter offered**, passed through with
233
290
  their own ids and kinds (`allow_once`, `allow_always`, `reject_once`). An answer must be one of those ids; any other is refused
234
291
  and the request stays open. An unanswered request is answered `cancelled` after 10 minutes, and a cancel,
@@ -258,6 +315,17 @@ reference, title, directory, last activity, and whether anything holds it open),
258
315
  chat client can offer a choice; `session.new` then names the chosen one (`resume_ref`). Claw and the chat client know no
259
316
  product: the agent's `kind` is the label `attach`.
260
317
 
318
+ **You do not have to add this agent.** Every agent made from the `claude` preset brings one with the same roots, adapter,
319
+ approval and environment, named `<id>-sessions`, so the conversations already on the machine show up on the phone by
320
+ themselves. `existing_sessions = false` on the `claude` agent turns that off; an explicit `claude-attach` agent reading the same
321
+ Claude config directory replaces it.
322
+
323
+ Each entry also says, for a conversation a terminal holds open, what Claude Code says that terminal is doing (`activity`:
324
+ `working`, `waiting` for the person at it, or `idle`; with `waiting`, `waiting_for` in its words, such as "input needed"). The
325
+ connector looks at Claude Code's session registry and projects directory every 10 seconds (file names and times only; nothing
326
+ is started) and reports a change at once instead of at the next heartbeat, so a conversation a terminal has just opened, or one
327
+ that starts waiting, reaches the phone within seconds.
328
+
261
329
  ```toml
262
330
  [[agents]]
263
331
  id = "claude-attach"
@@ -319,6 +387,18 @@ A transcript written within the last 10 seconds is refused too (a terminal that
319
387
  `session_live`, `recently_written` and `twin_check_failed`; the agent that this connector itself started for the conversation
320
388
  is not mistaken for a twin.
321
389
 
390
+ A conversation that a terminal has open when the phone asks for it is not refused but watched, read-only. Nothing is
391
+ started: the connector reads the transcript the terminal writes, from its end (opened read-only; nothing is ever written to it),
392
+ and sends each new line as the update an adapter would have sent (the user's words, the agent's words, the steps it starts
393
+ and how they end; thinking and sub-agents are left out), scrubbed for credentials like every event and marked `read_only`.
394
+ The last twenty updates already there are sent first, marked `replay`. `session_started` says `read_only: true` and
395
+ `read_only_reason: "session_live"`. A prompt, a mode change or a decision sent to it is refused with
396
+ `read_only_live_session` and reaches nothing that could write to the transcript; the server refuses them too
397
+ (`session_read_only`). The heartbeat still reports the conversation as `live`, and an attach agent's capabilities say `watch_live: true`, so a
398
+ client offers the watch only where the connector can do it. When the terminal no longer has it open the
399
+ watch ends (`session_closed`, reason `terminal_closed`), and taking it over is then a new session, through every check
400
+ above. A watched session stays read-only across a restart of the connector.
401
+
322
402
  The ask mode still applies: a conversation keeps the mode it was left in, often one that never asks (`bypassPermissions`),
323
403
  and the adapter says so while it sets the session up. That warning describes the mode that is about to be replaced, so it is
324
404
  set aside until the ask mode (`default`) is locked and read back from the adapter; a warning that comes after that, or a mode
@@ -335,6 +415,7 @@ session stops the agent process; it never deletes the conversation.
335
415
  | `run [--supervised]` | Connect and serve. Exit code 78 means starting it again will not help: the connector was revoked, Claw or the identity service refused the sign-in (at start, or for two minutes in a row while running), or there is no usable `agents.toml` or sign-in; the reason is kept in `needs-login.json`, and until `login` removes it `run` exits 78 at once without contacting Claw. `--supervised` marks a run the service started. Any other non-zero exit is worth a restart. A unit written by hand for 0.1.2 and older (`RestartPreventExitStatus=3`): replace it with `claw-connector service install`, which writes the same file. |
336
416
  | `service install\|uninstall\|status [--supervisor systemd\|launchd\|script] [--env NAME ...]` | Keep `run` up (see [Keeping it running](#keeping-it-running)). `install` stops a `run` started by hand so the service takes over; `status` also lists the last five disconnects and why. |
337
417
  | `status [--json]` | Running or not, paused, signed in, agents, adapter processes, the last disconnect and its reason (`last down:`), a `problem:` line when a new sign-in is needed, and a `warning:` line for each agent whose sessions will be refused because Claude's own settings default to `bypassPermissions` (see [Running without per-step approval](#running-without-per-step-approval)). |
418
+ | `doctor [--agent ID] [--json]` | Check this machine and print one fix per problem (see step 5 of the quick start), then each agent's adapter (step 2); exit 1 when something stops sessions. The report is kept in `doctor.json`; a running connector is signalled to check again and sends what it finds to Claw (each agent's `capabilities.health`). |
338
419
  | `pause` / `resume` | Stop every agent within a second and stay offline, or undo that. |
339
420
  | `stop` | Stop the running connector and every agent it started. |
340
421
  | `logout` | Stop, revoke this machine's connector at Claw (with a key, `DELETE /v1/remote-agents/connectors/:cid`; with a device sign-in, the connector-side `POST .../connectors/:cid/revoke`), ask the identity service to revoke the sign-in, and delete it from this machine. The next sign-in registers a new connector. When Claw cannot be reached or refuses (a Claw from before the connector-side route refuses a device sign-in), the sign-in is still deleted, but `logout` says the connector was not revoked, prints the `curl` that revokes it with a Claw API key of yours, keeps its id (so a later sign-in reuses it), and exits 1. |
@@ -347,12 +428,12 @@ the connector sees it within a heartbeat, stops every agent and exits without re
347
428
 
348
429
  ## Files
349
430
 
350
- `~/.config/claw-connector/` (or `$XDG_CONFIG_HOME/claw-connector/`): `agents.toml`, `credentials.json` (0600; a file other users can read is
351
- refused). `~/.local/state/claw-connector/` (or `$XDG_STATE_HOME/claw-connector/`): `connector.json` (the connector's identity and the
431
+ `~/.config/claw-connector/` (or `$XDG_CONFIG_HOME/claw-connector/`, or `$CLAW_CONNECTOR_CONFIG_DIR`): `agents.toml`, `credentials.json`
432
+ (0600; a file other users can read is refused), and `service-env.json` (0600, the agents' sign-in variables `service install` kept). `~/.local/state/claw-connector/` (or `$XDG_STATE_HOME/claw-connector/`): `connector.json` (the connector's identity and the
352
433
  commands handled), `sessions.json`, `outbox/` (events not yet acknowledged by Claw), `audit.jsonl` (spawn, directory
353
434
  decisions, every command received, every permission request and answer, every automatic approval, mode changes),
354
- `logs/`, `children.json`, `connector.pid`, `disconnects.jsonl`, `needs-login.json` (only after a run that needs a new sign-in), and with
355
- the script supervisor `supervisor.pid`, `keepalive.sh` and `run.log`. `CLAW_CONNECTOR_HOME=/some/dir` keeps all of it under one directory.
435
+ `logs/`, `children.json`, `connector.pid`, `disconnects.jsonl`, `needs-login.json` (only after a run that needs a new sign-in), `doctor.json`
436
+ (the last `doctor` report), and with the script supervisor `supervisor.pid`, `keepalive.sh`, `keepalive-last` (when it last ran) and `run.log`. `CLAW_CONNECTOR_HOME=/some/dir` keeps all of it under one directory.
356
437
 
357
438
  ## Keeping it running
358
439
 
@@ -365,32 +446,68 @@ the script supervisor `supervisor.pid`, `keepalive.sh` and `run.log`. `CLAW_CONN
365
446
  Log: `run.log` in the state directory.
366
447
  - **script** (a container without systemd; an `systemctl` that answers "Running in chroot" counts as none): `claw-connector supervise`
367
448
  runs in the background and restarts the connector 5 seconds after it dies; `keepalive.sh` in the state directory starts that
368
- supervisor unless it is alive (recorded pid and start time) or a sign-in is needed. Nothing restarts the supervisor after the container
369
- restarts, so call `keepalive.sh` from the container's startup command and from a 5-minute cron job. Log: `run.log`.
449
+ supervisor unless it is alive (recorded pid and start time) or a sign-in is needed. Where a cron daemon runs, `install` adds an
450
+ `@reboot` and a 5-minute line that call `keepalive.sh` to your crontab (once; `uninstall` removes them). Where none runs (most
451
+ containers), `install` says so: nothing brings the connector back after a restart until the container's startup command (or
452
+ `~/.bashrc`) runs `sh keepalive.sh`. Log: `run.log`.
453
+
454
+ **A container whose home directory does not outlive it** (a dev pod): when `agents.toml` and the sign-in sit on the container's own
455
+ file system (overlay, tmpfs), `init` and `service install` say so. Keep them on storage that survives the container with
456
+ `CLAW_CONNECTOR_CONFIG_DIR=/mnt/<your persistent volume>/claw-connector` (only the configuration and sign-in move; pid files, the outbox
457
+ and logs stay on the local disk), set in your shell profile and in the container's startup command, which then runs
458
+ `claw-connector service ensure` (after installing the package again, if it lived on the container's file system too).
370
459
 
371
460
  A successful `login` starts an installed service again, so after a revocation or a refused key signing in is the whole fix.
372
461
  `keepalive.sh` and the script supervisor start nothing while only a person can help (no `agents.toml`, not signed in,
373
462
  `needs-login.json`); under launchd every exit 78 is turned into a clean exit, which launchd does not restart. The service
374
463
  installed for a state directory is recorded in its `service.json`.
375
464
 
376
- The unit carries `PATH` (so the agents are found), `CLAW_CONNECTOR_HOME`, `XDG_CONFIG_HOME`, `XDG_STATE_HOME`, `CLAUDE_CONFIG_DIR`,
377
- `CODEX_HOME`, the CA and proxy variables and `LANG` from the shell that ran `install` (a proxy URL with a password in it only when named with `--env`, e.g. `--env HTTPS_PROXY`); `--env NAME` adds another, except a name that looks
378
- like a credential (`KEY`, `TOKEN`, `SECRET`, `PASSWORD`, `CREDENTIAL`), which is never written into a unit. Run `install` again after
379
- changing any of them, or after moving Node. With a service installed, `claw-connector stop` stops the connector until the next login,
465
+ The unit carries `PATH` (so the agents are found), `CLAW_CONNECTOR_HOME`, `CLAW_CONNECTOR_CONFIG_DIR`, `XDG_CONFIG_HOME`, `XDG_STATE_HOME`,
466
+ `CLAUDE_CONFIG_DIR`, `CODEX_HOME`, the CA and proxy variables (with `NODE_USE_SYSTEM_CA` and `NODE_USE_ENV_PROXY`), `LANG`, `IS_SANDBOX`, the npm registry and proxy settings, and every
467
+ plain setting of the agents (`ANTHROPIC_*`, `CLAUDE_CODE_*`, `OPENAI_*`, `CODEX_*`, `CURSOR_*` that is not named like a credential:
468
+ `ANTHROPIC_BASE_URL`, `ANTHROPIC_MODEL`, `CLAUDE_CODE_USE_BEDROCK`...) from the shell that ran `install` (a proxy URL with a password in
469
+ it only when named with `--env`, e.g. `--env HTTPS_PROXY`); `--env NAME` adds another.
470
+
471
+ The agents' own sign-in variables are never written into a unit. When the shell that runs `install` has `ANTHROPIC_API_KEY`,
472
+ `ANTHROPIC_AUTH_TOKEN`, `CLAUDE_CODE_OAUTH_TOKEN`, `ANTHROPIC_CUSTOM_HEADERS`, `OPENAI_API_KEY`, `CODEX_API_KEY`, `CURSOR_API_KEY`, the
473
+ AWS keys, a variable Codex's `config.toml` names as a provider's `env_key`, or an `--env` name that looks like a credential (`KEY`,
474
+ `TOKEN`, `SECRET`, `PASSWORD`, `CREDENTIAL`), they are kept in `service-env.json` beside `credentials.json` (mode 0600) and every `run`
475
+ gives them to the agents that do not set them in `[agents.env]` (a configuration directory its group or others may write is first made 0700; one owned by another account is refused). `install` prints their names, never their values; `service status`
476
+ lists them; `uninstall` deletes the file. Without this a connector that works in a terminal loses its agents' sign-in as a service,
477
+ and each session fails at its first message. `CLAW_CONNECTOR_KEY` is never copied. Run `install` again after changing any of them, or
478
+ after moving Node. With a service installed, `claw-connector stop` stops the connector until the next login,
380
479
  boot or keep-alive check; to stay offline use `pause`, or `service uninstall`.
381
480
 
382
481
  **Why it went down.** Every disconnect is appended to `disconnects.jsonl` in the state directory: `server_closed` (a Claw release or
383
482
  restart, `superseded` by another run, `token_expired`; reconnected at once), `stream_lost` (the network, with the error), `stopped`,
384
- `revoked`, `auth_failed`, `failed`, and `process_died` (the previous run never shut down: killed, crashed or the machine restarted;
385
- dated by its last heartbeat). `status` shows the latest, `service status` the last five.
483
+ `revoked`, `auth_failed`, `failed`, `process_died` (the previous run never shut down: killed, crashed or the machine restarted;
484
+ dated by its last heartbeat), and `connector_limit_reached` (Claw already holds the most connectors one person may have: remove a
485
+ machine you no longer use, in the chat with `/agents` or with `claw-connector logout` on it; the run stays up and tries again every 10
486
+ minutes instead of being restarted every 5 seconds). `status` shows the latest, `service status` the last five.
386
487
 
387
488
  ## Restarts and crashes
388
489
 
389
- A prompt counts as handled only when it reaches the agent (or is dropped by a cancel, a close or a stop), so a connector that
390
- dies with a prompt still queued is sent that prompt again by Claw instead of losing it. A permission request waiting
391
- for an answer lives in the adapter, which dies with the connector: the next run closes such a card (`cancelled`, by
392
- `connector_restart`), so a phone is not left with a button that does nothing. Session state, unacknowledged events and
393
- their sequence numbers are on disk; a session resumes in a fresh adapter on its next prompt.
490
+ A prompt counts as handled only when it reaches the agent (or is dropped by a cancel or a close), so a connector that
491
+ dies, or is stopped or restarted by its supervisor, with a prompt still queued is sent that prompt again by Claw instead of
492
+ losing it. A permission request waiting for an answer lives in the adapter, which dies with the connector: the next run
493
+ closes such a card (`cancelled`, by `connector_restart`), so a phone is not left with a button that does nothing. Session
494
+ state, unacknowledged events and their sequence numbers are on disk; a session resumes in a fresh adapter on its next prompt
495
+ (Claude Code's `session/resume`, the conversation intact).
496
+
497
+ The turn an agent is running is on disk too, from the moment its prompt reaches the agent until its `turn_end`. The next run
498
+ (after a crash, a kill, a pod restart, `stop`, or an upgrade) finds it and:
499
+
500
+ - carries it on when the agent was waiting for a person's permission for a tool call (not a plan or a question card), the agent
501
+ always asks (no `approval = "never"`, no `allow_unsafe_modes`) and its session was in a mode that asks, the connector is not
502
+ paused, and it was away no longer than a card lives (10 minutes): the agent is told the connector restarted and to go on, so
503
+ it asks again and a new card comes; the prompt's own `turn_end` still ends it. It only asks, so nothing runs that nobody
504
+ approved. Nothing is carried on before the run has signed in (a revoked connector carries on nothing);
505
+ - otherwise ends it with `turn_end` `{"stopReason": "error", "error": "connector_restarted"}`, naming its prompt (`cmd_id`,
506
+ `cmd_seq`), so a phone stops waiting for it. The agent is not re-run by itself; the next message resumes the conversation.
507
+
508
+ Every session used in the last 4 hours, and every one with a turn found this way, then gets one `session_recovered` event
509
+ (`interrupted`: a turn was cut off; `continuing`: it is being carried on; `cmd_id` and `cmd_seq` of that turn), which a chat
510
+ shows as "recovered". A stop by the kill switch (`pause`, a revocation) still ends every turn and drops the queue.
394
511
 
395
512
  Known limit: the working-directory check resolves the path and starts the adapter in the resolved path, but another process of
396
513
  the same user that can rewrite a directory in that path could swap a symlink between the check and the start. The