@agfpd/iapeer 0.2.98 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/README.md +21 -7
  2. package/docs/01-overview.md +86 -0
  3. package/docs/02-quickstart.md +110 -0
  4. package/docs/03-peers-runtimes-identity.md +117 -0
  5. package/docs/04-lifecycle-and-daemon.md +101 -0
  6. package/docs/05-system-prompt.md +82 -0
  7. package/docs/06-storage.md +115 -0
  8. package/docs/07-install-and-update.md +87 -0
  9. package/docs/08-peer-management.md +119 -0
  10. package/docs/09-runtimes.md +103 -0
  11. package/docs/10-messaging.md +69 -0
  12. package/docs/11-extensions-and-memory.md +60 -0
  13. package/docs/12-cli-reference.md +96 -0
  14. package/docs/13-architecture-and-contract.md +190 -0
  15. package/docs/14-configuration-and-logs.md +91 -0
  16. package/docs/README.md +97 -0
  17. package/docs/ru/01-/320/276/320/261/320/267/320/276/321/200.md +86 -0
  18. package/docs/ru/02-/320/261/321/213/321/201/321/202/321/200/321/213/320/271-/321/201/321/202/320/260/321/200/321/202.md +110 -0
  19. package/docs/ru/03-/320/277/320/270/321/200/321/213-/321/200/320/260/320/275/321/202/320/260/320/271/320/274/321/213-/320/270/320/264/320/265/320/275/321/202/320/270/321/207/320/275/320/276/321/201/321/202/321/214.md +117 -0
  20. package/docs/ru/04-/320/266/320/270/320/267/320/275/320/265/320/275/320/275/321/213/320/271-/321/206/320/270/320/272/320/273-/320/270-/320/264/320/265/320/274/320/276/320/275.md +101 -0
  21. package/docs/ru/05-/321/201/320/270/321/201/321/202/320/265/320/274/320/275/321/213/320/271-/320/277/321/200/320/276/320/274/320/277/321/202.md +82 -0
  22. package/docs/ru/06-/321/205/321/200/320/260/320/275/320/265/320/275/320/270/320/265.md +115 -0
  23. package/docs/ru/07-/321/203/321/201/321/202/320/260/320/275/320/276/320/262/320/272/320/260-/320/270-/320/276/320/261/320/275/320/276/320/262/320/273/320/265/320/275/320/270/320/265.md +87 -0
  24. package/docs/ru/08-/321/203/320/277/321/200/320/260/320/262/320/273/320/265/320/275/320/270/320/265-/320/277/320/270/321/200/320/260/320/274/320/270.md +119 -0
  25. package/docs/ru/09-/321/200/320/260/320/275/321/202/320/260/320/271/320/274/321/213.md +103 -0
  26. package/docs/ru/10-/320/276/320/261/320/274/320/265/320/275-/321/201/320/276/320/276/320/261/321/211/320/265/320/275/320/270/321/217/320/274/320/270.md +69 -0
  27. package/docs/ru/11-/321/200/320/260/321/201/321/210/320/270/321/200/320/265/320/275/320/270/321/217-/320/270-/320/277/320/260/320/274/321/217/321/202/321/214.md +60 -0
  28. package/docs/ru/12-cli-reference.md +96 -0
  29. package/docs/ru/13-/320/260/321/200/321/205/320/270/321/202/320/265/320/272/321/202/321/203/321/200/320/260-/320/270-/320/272/320/276/320/275/321/202/321/200/320/260/320/272/321/202.md +190 -0
  30. package/docs/ru/14-/320/272/320/276/320/275/321/204/320/270/320/263/321/203/321/200/320/260/321/206/320/270/321/217-/320/270-/320/266/321/203/321/200/320/275/320/260/320/273/321/213.md +91 -0
  31. package/docs/ru/README.md +97 -0
  32. package/package.json +8 -2
  33. package/src/cli/index.ts +20 -0
  34. package/src/tui/onboard/app.tsx +260 -0
  35. package/src/tui/onboard/run.tsx +72 -0
  36. package/tsconfig.json +3 -1
@@ -0,0 +1,115 @@
1
+ # 06 — Storage
2
+
3
+ [Русский](ru/06-хранение.md) · **English**
4
+
5
+ iapeer state lives in two places: the shared host directory `~/.iapeer/` and the `.iapeer/` directory inside each peer's working folder.
6
+
7
+ ## Two storage levels
8
+
9
+ **The shared level** `~/.iapeer/` — everything concerning the host as a whole: the peer registry, daemon state, shared settings, installed runtime packages.
10
+
11
+ **The peer level** `<peer-folder>/.iapeer/` — everything concerning one peer: its profile, doctrine, its plugins' state, its sessions' logs.
12
+
13
+ The same plugin can store data at both levels: shared in `~/.iapeer/`, peer-bound in the peer's folder.
14
+
15
+ ## The shared-level layout
16
+
17
+ ```text
18
+ ~/.iapeer/
19
+ ├── IAPEER.md host doctrine (shared by all peers; prompt layer 2)
20
+ ├── peers-profiles.json registry of all peers (written under a lock)
21
+ ├── peers-profiles.lock registry lock file
22
+ ├── memory-provider.json memory slot: who the provider is (if claimed)
23
+ ├── fragments/ shared doctrine fragments (prompt layer 5)
24
+ ├── peers/
25
+ │ └── <name>/ a peer's default working folder
26
+ │ └── .iapeer/ (peer-level layout — see below)
27
+ ├── state/
28
+ │ ├── iapeer/ daemon state (router.sock, router.json …)
29
+ │ └── <plugin>/ a plugin's mutable state
30
+ ├── logs/
31
+ │ ├── iapeer/ daemon logs: lifecycle.log, delivery.log, exits.log
32
+ │ └── <name>/ an infrastructure peer's logs (by name)
33
+ ├── cache/
34
+ │ ├── iapeer/ core cache
35
+ │ └── <plugin>/ plugin cache (safe to delete)
36
+ ├── plugins/
37
+ │ ├── iap/ the core IAP plugin's install
38
+ │ └── <plugin>/ a plugin's install (immutable)
39
+ └── runtimes/
40
+ ├── claude/
41
+ │ └── plugins/<plugin>/ a plugin's config under the claude runtime
42
+ ├── codex/
43
+ │ └── plugins/<plugin>/
44
+ ├── telegram/
45
+ │ └── runtime.json a runtime package's manifest
46
+ └── notifier/
47
+ └── runtime.json
48
+ ```
49
+
50
+ A few nodes worth highlighting:
51
+
52
+ - **`peers-profiles.json`** — the registry. Written only through a single writer under the file lock `peers-profiles.lock`; direct editing is forbidden at the core level.
53
+ - **`state/iapeer/`** — where the daemon lives: its local socket `router.sock` and config `router.json`.
54
+ - **`logs/<name>/`** — an infrastructure peer's logs are placed by peer name at the shared level (not in the peer's folder), because it's the output of a launchd service.
55
+ - **`runtimes/<runtime>/`** — dual ownership: the root belongs to the runtime itself (Telegram keeps bot tokens and poll state here), while the `plugins/<plugin>/` subfolder is the plugins' space, holding their config under this runtime.
56
+
57
+ ## The peer-level layout
58
+
59
+ ```text
60
+ <peer-folder>/.iapeer/
61
+ ├── peer-profile.json the peer profile — the source of truth about it
62
+ ├── IAPEER.md personal doctrine (prompt layer 2)
63
+ ├── fragments/ personal doctrine fragments (layer 5)
64
+ ├── plugins/
65
+ │ ├── iap/
66
+ │ └── <plugin>/ a plugin's data for this peer
67
+ ├── state/
68
+ │ ├── iap/
69
+ │ └── <plugin>/
70
+ ├── logs/
71
+ │ ├── iap/
72
+ │ └── <plugin>/
73
+ ├── cache/
74
+ │ ├── iap/
75
+ │ └── <plugin>/
76
+ └── runtimes/
77
+ ├── claude/
78
+ │ ├── launch.env launch args and env vars for this runtime
79
+ │ └── plugins/<plugin>/
80
+ └── codex/
81
+ ├── launch.env
82
+ └── plugins/<plugin>/
83
+ ```
84
+
85
+ - **`peer-profile.json`** — the peer's card and the single source of truth about it (the fields are covered in [03 — Peers, runtimes, identity](03-peers-runtimes-identity.md)).
86
+ - **`launch.env`** — a `KEY=VALUE` file for a specific peer+runtime pair: extra launch arguments (`PEER_START_ARGS`) and environment variables. So one peer can be given special launch flags without touching the rest.
87
+
88
+ A peer's working folder doesn't have to live under `~/.iapeer/peers/`. By default `iapeer create` puts it there, but any folder can be made a peer with `iapeer init` — then `.iapeer/` appears right inside it.
89
+
90
+ ## Who owns what
91
+
92
+ The ownership boundary is simple: everyone owns the contents of their own folder and stays out of others'.
93
+
94
+ | Owner | What belongs to it |
95
+ |---|---|
96
+ | **Core (`iapeer`)** | `peers-profiles.json`, `state/iapeer/`, `logs/iapeer/`, `cache/iapeer/` |
97
+ | **Runtime** | the root of `runtimes/<runtime>/` — except the `plugins/` subfolder |
98
+ | **Plugin** | its own `<plugin>/` folder in every category (`state`, `logs`, `cache`, `plugins`) at both levels |
99
+
100
+ The name `iapeer` in the categories is reserved for the core. A runtime never touches `plugins/<plugin>/` inside its space, and a plugin never touches the runtime root.
101
+
102
+ ## Data classes by lifetime
103
+
104
+ Install is separated from mutable data — important for updates and for cleanup:
105
+
106
+ - **Install** (`plugins/<plugin>/`) — immutable. It's the plugin's unpacked code; an update replaces it whole.
107
+ - **State** (`state/`) — mutable data worth protecting: losing it is losing work.
108
+ - **Logs** (`logs/`) — process output; useful for analysis but not critical. What's written to `lifecycle.log`, `delivery.log`, `exits.log` and how to configure their rotation — [14 — Configuration and logs](14-configuration-and-logs.md).
109
+ - **Cache** (`cache/`) — recoverable data; it can be deleted with no consequence, the system rebuilds it.
110
+
111
+ This split means updating a plugin is replacing its install folder without touching its state; and freeing space is deleting the cache without risking data.
112
+
113
+ ## The registry as a derivative
114
+
115
+ Worth repeating the key storage principle: **the registry `peers-profiles.json` is a projection of the local profiles, not a separate source of truth.** The source of truth about a peer is its `peer-profile.json`. The registry can be reprojected from the profiles at any time with `iapeer verify --fix`. On a divergence between the registry and a profile, the profile wins. So losing or corrupting the registry isn't fatal — it's rebuilt by walking the peers' working folders.
@@ -0,0 +1,87 @@
1
+ # 07 — Install and update
2
+
3
+ [Русский](ru/07-установка-и-обновление.md) · **English**
4
+
5
+ Install is two steps: lay down the core and prepare the host. You don't bring the daemon up by hand — onboarding does that. Below: both steps, plus updating the core and rolling back.
6
+
7
+ ## What you need first
8
+
9
+ - **macOS.** Processes are managed by launchd — there's no other platform yet.
10
+ - **[bun](https://bun.sh)** on `PATH`. The core runs through bun; the package ships no compiled JS.
11
+ - **`~/.local/bin` on `PATH`.** The `iapeer` binary is installed here. Not on `PATH` — the command won't be found after install.
12
+ - **At least one agent runtime** — Claude Code or Codex CLI. Without it, `iapeer create` and `iapeer init` refuse with an explicit error.
13
+
14
+ ## Step 1. Install the core
15
+
16
+ ```bash
17
+ npx @agfpd/iapeer
18
+ ```
19
+
20
+ A run with no arguments performs the install. It does exactly three things:
21
+
22
+ 1. **Layout.** Creates `~/.iapeer/` with all its directories: registry, state, logs, cache, runtime spaces.
23
+ 2. **Binary.** Builds `iapeer` and atomically places it in `~/.local/bin/iapeer`. The previous version stays beside it as `iapeer.prev` — a rollback reserve. Atomicity matters: between the old and new versions there's no moment when a stub sits at the path.
24
+ 3. **Daemon plist.** Writes `~/Library/LaunchAgents/com.agfpd.iapeer.plist` but **doesn't load** it. Loading is taken on by `iapeer onboard` (step 2): on a running host the daemon may already be spinning, and migrating to a new binary is a deliberate act, not a side effect of install.
25
+
26
+ On install a local binary signature is created once (for macOS permissions). The signature is stable and survives updates.
27
+
28
+ The install is idempotent — a re-run is safe.
29
+
30
+ The same operation is available as `iapeer install` if the binary is already in place. But the very first time is always through `npx` or from source: a compiled binary can't rebuild itself.
31
+
32
+ ## Step 2. Prepare the host
33
+
34
+ ```bash
35
+ iapeer onboard
36
+ ```
37
+
38
+ `onboard` prepares the host for agents. First of all it **brings up the daemon** — loads the plist into launchd, after which launchd holds the daemon itself, bringing it back up on a crash and a reboot. No separate `launchctl` command is needed for this. Then onboard registers the extension marketplace in Claude and Codex, provisions the scheduler (`notifier`), creates a human peer on Telegram, and installs the memory provider. The daemon start always runs; the rest are on by default and turned off with flags (`--no-notifier`, `--no-telegram`, `--no-memory`).
39
+
40
+ Check the daemon is alive and the host is ready:
41
+
42
+ ```bash
43
+ iapeer status
44
+ ```
45
+
46
+ You'll see the core version, the daemon's health, and the memory-slot state. The exit code is non-zero if the daemon is unhealthy — handy for check scripts.
47
+
48
+ A breakdown of each onboard step is in [02 — Quick start](02-quickstart.md); runtimes and memory in detail — in [09](09-runtimes.md) and [11](11-extensions-and-memory.md).
49
+
50
+ ## Updating the core
51
+
52
+ ```bash
53
+ iapeer update # to the latest version
54
+ iapeer update 0.2.80 # to a specific version
55
+ iapeer update --force # reinstall even if the version is already in place
56
+ ```
57
+
58
+ An update pulls the core version from npm and restarts the daemon onto it. Step by step:
59
+
60
+ 1. Learns the target version from npm. If the installed one already matches and there's no `--force` — it does nothing.
61
+ 2. Downloads and builds the new binary. This is done via `npm pack`, not `npx`: the binary doesn't rebuild itself.
62
+ 3. Restarts the daemon onto the new binary.
63
+ 4. Restarts the infrastructure launchd jobs the core owns (`com.iapeer.*` — the `timer`, `watcher`, Telegram routers), moving them onto the just-replaced binary. Their plists launch the same core binary as the daemon — without a restart a job would stay on the old code. It's the job that's restarted (booted out and back in); the runtimes' **package code** (telegram-runtime, notifier-runtime) is not updated by this — that's the separate `update-runtime` operation. The memory provider (`iapeer-memory`) and ordinary peers' plists aren't touched at all. If the daemon didn't come up in step 3, this step is skipped — no point moving jobs onto a binary the daemon didn't revive on.
64
+ 5. Verifies the daemon actually came up healthy — not by the restart command exiting successfully, but by a probe: it connects to the daemon's socket (the one the daemon opens right before the readiness signal). The daemon counts as healthy only when the socket accepts a connection **twice in a row** — a single probe would also pass for a daemon that bound the socket and immediately crashed. The check is given 15 seconds; if a run of successful probes doesn't accumulate in that time, the command says so directly and advises rolling back.
65
+
66
+ An update touches **only the core**. The daemon plist and third-party packages stay as they are.
67
+
68
+ Naming a specific version (`iapeer update <version>`) is useful when you need to roll back deeper than one step, or pin a proven version.
69
+
70
+ ## Rollback
71
+
72
+ ```bash
73
+ iapeer rollback
74
+ ```
75
+
76
+ Restores the previous binary (that `iapeer.prev`), restarts the daemon onto it, and checks health. Rollback goes **one level back** — one previous version is kept. It's a local "undo the last update" while a fixed version is published to npm. To go deeper — `iapeer update <version>`.
77
+
78
+ ## If the daemon didn't come up after an update
79
+
80
+ The sign — `iapeer status` shows an unhealthy daemon, or `update` finished with a warning. The order of action:
81
+
82
+ ```bash
83
+ iapeer rollback # return to the prior binary
84
+ # if the daemon is still down — bring it up by hand:
85
+ launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/com.agfpd.iapeer.plist
86
+ iapeer status # confirm it's revived
87
+ ```
@@ -0,0 +1,119 @@
1
+ # 08 — Peer management
2
+
3
+ [Русский](ru/08-управление-пирами.md) · **English**
4
+
5
+ The everyday operations on peers. Full command signatures with flags are in [12 — CLI](12-cli-reference.md); here — when and why to use each.
6
+
7
+ ## Creating a peer: create and init
8
+
9
+ A peer can be created with two commands, and the difference between them is where the working folder comes from.
10
+
11
+ **`iapeer create <name>`** creates a peer from anywhere. The command picks the folder itself (by default `~/.iapeer/peers/<name>`, or pass `--path`), scaffolds it, and initializes it. This is the main way.
12
+
13
+ ```bash
14
+ iapeer create assistant --runtime claude
15
+ iapeer create reviewer --runtime codex --description "Code and architecture review"
16
+ iapeer create helper --path ~/projects/helper --runtime claude
17
+ ```
18
+
19
+ **`iapeer init`** turns the current (or a given) folder into a peer. Handy when a peer already has its own project folder and you want `.iapeer/` to appear right inside it.
20
+
21
+ ```bash
22
+ cd ~/projects/myagent
23
+ iapeer init
24
+ ```
25
+
26
+ The peer name is taken from the folder name (the normalized `basename` — `myagent`); there's no separate flag for the name — to name a peer otherwise, rename the folder. The runtime is detected automatically from the installed ones (Claude/Codex), and set with `--runtime` when needed.
27
+
28
+ In essence `create` is "pick/create a folder, then `init`". Both commands perform the same peer setup:
29
+
30
+ - write the profile `peer-profile.json`;
31
+ - enter the peer into the registry;
32
+ - set up the connection to the daemon over MCP (so the agent gets `send_to_peer`);
33
+ - create the doctrine file `IAPEER.md` to be filled in;
34
+ - if the memory slot is claimed — attach shared memory and do the related runtime setup.
35
+
36
+ No-clobber protection: if another peer already lives in the target folder (a different name in the profile), the command refuses rather than overwrite.
37
+
38
+ The `--no-bootstrap` flag creates a peer without starting its service (relevant for infrastructure peers, which otherwise come up under launchd right away).
39
+
40
+ ## Reviewing peers: list
41
+
42
+ ```bash
43
+ iapeer list # a table
44
+ iapeer list --json # machine-readable format
45
+ ```
46
+
47
+ Each line is a peer, its runtimes, and the state of each: `●` alive, `○` asleep, `✕` stopped. Run in an interactive terminal, `list` opens a control panel (arrows to navigate, Enter to attach, `/` to search, `q` to quit); in non-interactive mode or with `--json` it prints a table for scripts.
48
+
49
+ ## Integrity check: verify
50
+
51
+ ```bash
52
+ iapeer verify # report divergences
53
+ iapeer verify --fix # repair
54
+ ```
55
+
56
+ `verify` checks two things: profiles' conformance to the standard, and divergences between the registry and the local profiles. With `--fix` the command self-heals the registry from the profiles (and migrates profiles from the old runtime-field format to the current one along the way). It's the standard way to put the registry in order if it's gone out of sync.
57
+
58
+ ## Stop and start: stop, start
59
+
60
+ ```bash
61
+ iapeer stop assistant # stop
62
+ iapeer start assistant # clear the stop
63
+ iapeer stop --all # stop all peers
64
+ iapeer start --all # clear the stop on all
65
+ ```
66
+
67
+ Behavior depends on the runtime class:
68
+
69
+ - **A claude/codex agent.** `stop` sets a persistent stop flag and closes the session — the daemon won't wake the peer until `start` is run. `start` clears the flag: the peer can be woken again, but **the session doesn't start right away** — the daemon brings it up on the first message.
70
+ - **An infrastructure peer.** `stop` removes the service from launchd, `start` loads it back.
71
+
72
+ You can address a specific runtime: `iapeer stop assistant codex`.
73
+
74
+ ## Unconditional restart: new
75
+
76
+ ```bash
77
+ iapeer new assistant
78
+ ```
79
+
80
+ `new` is the emergency lever for a hung or dead session. Unlike the soft path (where a live agent reads the command itself and restarts), `new` acts mechanically, around the agent: it cleanly tears down the current session and brings up a fresh one. The command reports success only when the fresh session has actually come up and is ready. It's the rescue for when an agent is hung so badly it reads nothing.
81
+
82
+ ## Lazy soft restart: refresh
83
+
84
+ ```bash
85
+ iapeer refresh assistant # mark a peer for a fresh start on its next wake
86
+ iapeer refresh --all # mark all peers
87
+ ```
88
+
89
+ `refresh` is the deferred lever for "the doctrine was updated, I want the team to re-read it, but without a jolt". It **only sets a marker**: it doesn't kill a live session, doesn't wake sleepers, doesn't do an immediate restart. The effect arrives on its own — on the peer's **next natural wake** (after a normal idle sleep, or on the next message to a sleeper): instead of resuming the prior conversation, a fresh session comes up that re-reads the doctrine and fragments from disk.
90
+
91
+ Why it's separate from `new`: `new` is immediate and hard (it tears the current work), and a bulk `new --all` would bring the whole team up at once. `refresh` is lazy and soft: it doesn't touch live work, there's no wake spike, freshness reaches each peer at its usual bring-up moment. The marker overrides "resume" for **all** peer types, including humans-on-Telegram, who otherwise resume the old session until an explicit `/new`. Routers (notifier/telegram) and absent runtimes are skipped — they have no doctrine. Using `refresh` is a deliberate operator step (e.g. after releasing an updated doctrine), not an automatic side effect.
92
+
93
+ ## Removal: remove
94
+
95
+ ```bash
96
+ iapeer remove assistant # remove the registry entry
97
+ iapeer remove assistant --force # remove even if the peer is currently alive
98
+ ```
99
+
100
+ `remove` deletes a peer's registry entry. Important properties:
101
+
102
+ - **It doesn't delete the folder.** The peer's data (its `.iapeer/`, history, memory) stays on disk — `remove` clears only the registry entry. If the folder was disposable, delete it yourself.
103
+ - **It refuses a live peer.** Removing a running peer's entry would sever its session from routing. The command refuses while the peer is alive; you can override with `--force`, but it's cleaner to stop it first.
104
+ - **Idempotent.** Removing a non-existent peer isn't an error — it's a successful no-op.
105
+
106
+ The typical use is to clean up "zombie" entries left from finished disposable peers.
107
+
108
+ ## Attaching to a session: attach
109
+
110
+ ```bash
111
+ iapeer attach assistant
112
+ iapeer attach assistant codex # a specific runtime
113
+ ```
114
+
115
+ `attach` brings the peer up if it's asleep, resumes its prior conversation, and opens its session under the supervisor right in your terminal — like an ordinary console. Detach with `Ctrl-]` — or simply close the terminal; the session keeps running in the background either way, and `iapeer attach` reconnects you whenever you need it.
116
+
117
+ ## The boundary: a foreign launchd is off-limits
118
+
119
+ The commands `stop`, `start`, `new` **refuse to touch peers managed by a foreign launchd plist** (e.g. live peers under a separate lifecycle manager). For such peers the core is only a reader: intervening would mean a fight with launchd over process control. This is a deliberate guard, not a limitation — it keeps the core from accidentally breaking what it doesn't own.
@@ -0,0 +1,103 @@
1
+ # 09 — Runtimes
2
+
3
+ [Русский](ru/09-рантаймы.md) · **English**
4
+
5
+ A runtime is a peer's mode of presence. The core knows four: `claude`, `codex`, `telegram`, `notifier`. The first two are AI agents, the last two infrastructure routers.
6
+
7
+ ## Agent runtimes: claude, codex
8
+
9
+ The daemon brings an agent up on an incoming message, in a session under the pty supervisor, and closes it after an hour idle. The system prompt ([05](05-system-prompt.md)) is swapped through the runtime's standard mechanism.
10
+
11
+ **Claude** launches with the `--system-prompt-file` flag, which replaces the built-in system prompt with the text assembled by iapeer. Conversation resume goes through `--continue` (the last session is taken), not by selecting a session by id. The conversation history lives in `~/.claude/projects/<folder>/*.jsonl`.
12
+
13
+ **Codex** gets the prompt via the `model_instructions_file` setting (`codex -c model_instructions_file=<file>`), which replaces the compiled base instructions. Resume is `codex resume --last`. History is in `~/.codex/sessions/`. Codex's connection to the daemon over MCP goes through a token header: Codex gates HTTP-MCP behind a token check, so the core supplies a non-functional "empty" token (it carries no protective role), and the daemon identifies the peer by the `X-IAPeer-Identity` header.
14
+
15
+ When setting up Codex, the core host-wide silences the startup update-check dialog (`check_for_update_on_startup = false` in `config.toml`). The interactive "an update is available, press Enter" is a gate at load: a Codex session the daemon woke headless would hang on it, never reaching readiness, until a human connected. Codex updates stay manual (`codex update` or brew) rather than a prompt at startup. A value the operator already set, the core doesn't rewrite.
16
+
17
+ Launch an agent in the current folder by hand, as a fresh session:
18
+
19
+ ```bash
20
+ iapeer claude # bring up the current folder's peer on Claude
21
+ iapeer codex # on Codex
22
+ ```
23
+
24
+ Fine launch flags for a specific peer+runtime pair are set in `<folder>/.iapeer/runtimes/<runtime>/launch.env` ([06](06-storage.md)).
25
+
26
+ ### Which runtime is chosen when a peer is created
27
+
28
+ When a peer is created (`create` / `init`), the primary runtime is resolved by layers:
29
+
30
+ 1. An explicit `--runtime` — it's taken, but first it's checked that the runtime is installed; if not, the command finishes with a clear error rather than silently accepting a non-existent runtime.
31
+ 2. Otherwise — a marker in the folder (`.claude` / `.codex`): a folder already configured for a runtime keeps it. The marker takes priority and isn't re-checked for installedness.
32
+ 3. Otherwise — from those installed on the host: exactly one installed — it; none — a clear error (not a silent Claude on a host with no runtime); both installed — Claude by default (choose Codex explicitly with `--runtime codex`).
33
+
34
+ The core determines a runtime's installedness by its launcher — the `IAPEER_<RUNTIME>_BIN` variable, the native installer location, then `PATH` — and by the global config directory (`~/.claude` / `~/.codex`).
35
+
36
+ ## Infrastructure runtimes: telegram, notifier
37
+
38
+ These are long-lived router processes, not AI. They run continuously under launchd; the daemon doesn't bring them up or sleep them.
39
+
40
+ **telegram** — the Telegram ↔ peers bridge. It requires the peer to be of `natural` nature (a human): an attempt to give an AI agent a Telegram presence is rejected. It runs as `telegram-runtime run`.
41
+
42
+ **notifier** — the scheduler. It provisions the service peers `timer` (fires on a schedule) and `watcher` (watches for events). It runs as `notifier-runtime run`.
43
+
44
+ Infrastructure peers' plists, unlike the daemon's plist, are **loaded automatically** on `iapeer create --runtime telegram|notifier` and on `iapeer onboard` (unless `--no-bootstrap` is passed). So no separate `launchctl bootstrap` is needed for them.
45
+
46
+ ## Several runtimes on a peer
47
+
48
+ A peer profile has two runtime fields: `runtimes` (all the peer can be present on) and `default_runtime` (the primary — where messages route and which comes up first). Extending the options and switching the primary runtime are different commands.
49
+
50
+ ### add-runtime — add a runtime
51
+
52
+ ```bash
53
+ iapeer add-runtime codex --peer assistant # for one peer
54
+ iapeer add-runtime codex --all # for all peers
55
+ ```
56
+
57
+ Adds an agent runtime to existing peers: appends it to `runtimes`, scaffolds the runtime space, and does the full Codex setup (pre-trusting the folder, the native-memory lever, attaching memory, MCP). The command **doesn't touch** `default_runtime` — this is extending options, not changing routing. Idempotent. Infrastructure peers are skipped: giving them an agent runtime is an operator decision via `create`, not a bulk operation.
58
+
59
+ ### default-runtime — switch the primary
60
+
61
+ ```bash
62
+ iapeer default-runtime codex --peer assistant # for one
63
+ iapeer default-runtime codex --all # all peers
64
+ ```
65
+
66
+ Switches the primary runtime — where routing goes and which runtime comes up first. A bulk `--all` switch is exactly the "transplant" of the whole team from one agent runtime to another; it's symmetric back (`default-runtime claude --all`). The command refuses if the peer doesn't declare that runtime (run `add-runtime` first), and skips infrastructure peers. The registry is fixed in the same command, so routing switches at once.
67
+
68
+ ## Connecting Telegram: connect
69
+
70
+ ```bash
71
+ iapeer connect telegram assistant --token <bot-token>
72
+ ```
73
+
74
+ Connects a Telegram bot to a peer in one pass: registers the bot, writes it into the peer profile, restarts the router. From a human only the token is needed (from `@BotFather`). Afterward — send the bot the **first message**: Telegram doesn't let a bot start a chat first, so the channel comes alive only after an inbound message from a human.
75
+
76
+ The command is idempotent, and behavior depends on the token. **The same token** is a clean no-op: the bot's credentials file doesn't change, the live router already runs on these credentials, no restart. **A new or changed token** requires a router restart (credentials are read only at start) — a second's pause in delivery for all Telegram peers; inbound messages aren't lost (long-polling remembers the offset, Telegram holds messages for up to 24 hours). An invalid token is rejected at once, with the verbatim reason from Telegram.
77
+
78
+ ## Runtime packages
79
+
80
+ Infrastructure runtimes ship as separate npm packages (`*-runtime`). The core installs and updates them:
81
+
82
+ ```bash
83
+ iapeer install-runtime notifier # install the package + provision its peers
84
+ iapeer update-runtime notifier # update with a peer restart
85
+ iapeer update-runtime --all # update all runtime packages
86
+ ```
87
+
88
+ `install-runtime` installs the package and provisions the peer set it declares (notifier provisions `timer` and `watcher`); the `--no-bootstrap` flag installs the package without loading its peers into launchd. A runtime with no declared set (telegram) is "operator-adds" mode: peers are set up by hand with `iapeer create <name> --runtime telegram`.
89
+
90
+ ### Deploying a runtime package: always from the cloud, like `iapeer update`
91
+
92
+ A runtime package's delivery follows the same **release ≠ deploy** split as the core (see [07 — Install and update](07-install-and-update.md)). `iapeer update-runtime <runtime>` is the runtime analog of `iapeer update`: a deploy to the host **from the cloud (npm)**, not from a local working tree. It:
93
+
94
+ 1. **version-gate** — compares npm-latest with the `version` stamp in the manifest (`~/.iapeer/runtimes/<runtime>/runtime.json`); latest installed → no-op (`already-latest`). With no stamp the gate is skipped and reinstall runs idempotently (and says so) — so stamping `version` in the manifest is the package's duty (otherwise both `update-runtime` gating and version observability in `iapeer status` and the per-peer version audit break).
95
+ 2. **re-install** — a forced `npx @agfpd/<runtime>-runtime`: **pulls the package from npm** and lets it self-deploy the new binary + manifest. The source is the published package, **not** a working tree.
96
+ 3. **re-provision** — idempotently the same path as `install-runtime` (sync descriptions, re-self-config; live peers aren't clobbered).
97
+ 4. **restart** — restarts the runtime's registered peers with ordinary `stop`/`start`. They're **host-aware**: a peer under the supervisor is shut down correctly (kill the supervisor and the child) and brought back up on the new binary — no separate procedure needed.
98
+
99
+ Accordingly, **a local self-install from a working tree is a dev probe, not a deploy** (the analog of running the core from source, `bun src/...`). Deploy a runtime from the cloud: first release (`npm publish` — to the cloud, live hosts untouched), then deploy (`update-runtime` — pull from the cloud + restart). Bypassing npm (activating local code via self-install) desyncs npm and the host and isn't reproducible on a new host: a clean host brings a runtime up only from npm (`install-runtime`, then `update-runtime`). The core (`iapeer update`) deliberately does NOT touch runtime packages — the symmetry is intentional.
100
+
101
+ A runtime launcher's path resolves from `PATH` by default; override it (e.g. for launchd with a minimal `PATH`) with the `NOTIFIER_RUNTIME_BIN` / `TELEGRAM_RUNTIME_BIN` variables — see [14 — Configuration and logs](14-configuration-and-logs.md).
102
+
103
+ The runtime package contract — the manifest format, the duties, the commands — is in [13 — Architecture and contract](13-architecture-and-contract.md).
@@ -0,0 +1,69 @@
1
+ # 10 — Messaging
2
+
3
+ [Русский](ru/10-обмен-сообщениями.md) · **English**
4
+
5
+ Peers exchange messages over the IAP protocol. Agents do it with the `send_to_peer` tool, the operator with the `iapeer send` command. Both paths go through the same daemon routing.
6
+
7
+ ## Two ways to send
8
+
9
+ **`send_to_peer`** — the MCP tool every claude/codex agent gets (through its connection to the daemon). This is the main channel between agents.
10
+
11
+ ```text
12
+ send_to_peer(personality="assistant", message="...", topic?, attachments?)
13
+ ```
14
+
15
+ **`iapeer send`** — sending from the command line, the same delivery path around the agent session (works even when the daemon's HTTP listener is unavailable):
16
+
17
+ ```bash
18
+ iapeer send assistant --message "text"
19
+ iapeer send assistant --message-file ./report.md # body from a file; '-' = stdin
20
+ iapeer send assistant --message "see attachment" --attachment ./report.pdf
21
+ iapeer send assistant --message "..." --topic "release" --from claude-reviewer
22
+ ```
23
+
24
+ The `--from` flag sets the sender (`<runtime>-<name>`); by default it's the identity of the current folder's peer. `--attachment` is repeatable. `--message-file`, with the body from a file or stdin, is handy for long, multi-line messages.
25
+
26
+ ## The delivery path
27
+
28
+ The daemon finds the recipient in the registry and looks at its state:
29
+
30
+ - **alive** — the daemon writes the message into the peer's session through the supervisor socket onto the session's input. The daemon waits for confirmation that the input was accepted (the input field cleared); the answer is `delivered`.
31
+ - **busy** (the agent is mid-turn, the input field isn't empty) — the message goes into the composer queue and is delivered as soon as the agent is free; the answer to the sender is `queued`. The queue waits up to 120 seconds, then force-delivers.
32
+ - **asleep** — the daemon brings the session up, re-checks the recipient, delivers; the answer is `delivered, woke`.
33
+ - **stopped** — the daemon refuses and tells the sender directly.
34
+
35
+ The key guarantee: `delivered` means the message actually landed in a live session (the daemon waited for confirmation), not that it was just sent into the void. If the session didn't confirm receipt in the allotted time, the daemon treats it as dead and doesn't lie about success.
36
+
37
+ In every message's envelope travels the sender's nature (`intelligence`) — the recipient knows whether a human, an agent, or a service process is writing to it.
38
+
39
+ ## Message limits
40
+
41
+ The protocol has bounds: the message body — up to 16000 characters, the topic — up to 200, attachments — up to 20 at a time. A message over the limit must be split or its content passed as a file attachment.
42
+
43
+ ## Sending into Telegram only with your own bot
44
+
45
+ A peer can't send messages into the Telegram domain if it has no bound Telegram bot. This is a guard: a reply to a human goes through a specific agent's bot, and a peer without such a binding doesn't write into Telegram — delivery to it is refused. The binding is made with `iapeer connect telegram` (see [09 — Runtimes](09-runtimes.md)).
46
+
47
+ ## Control commands
48
+
49
+ Session control goes on a separate channel from messages — it's not text into the conversation but a control action:
50
+
51
+ ```bash
52
+ iapeer interrupt assistant # interrupt the current turn (Escape), context intact
53
+ iapeer compact assistant # compact the conversation
54
+ ```
55
+
56
+ `interrupt` stops a turn that's gone off-track or hung, without losing context. `compact` compacts the conversation; if the peer is asleep after a clean sleep, the daemon resumes it first, then compacts, and for an empty session it honestly answers that there's nothing to compact.
57
+
58
+ Separate are **aliases** — shortcuts like `/alias-new`, `/alias-compact`, defined in the peer profile. These aren't control but text templates: the runtime expands an alias into its full text and delivers it to the peer as an ordinary message. So with one short command the operator runs a pre-written scenario (e.g. "save state to memory and restart fresh").
59
+
60
+ ## Ephemeral workers
61
+
62
+ A peer with the wake policy `wake_policy: "ephemeral"` in its profile works differently from an ordinary agent — it's meant for one-off tasks:
63
+
64
+ - **always fresh.** Each task brings up a fresh session, with no resume of the past conversation.
65
+ - **one at a time.** Tasks aren't delivered into a live session in parallel — they line up in a durable queue (it survives a daemon restart) and are processed strictly one by one. The daemon feeds the queue on supervisory ticks.
66
+ - **finish after the reply.** Having sent its single reply, the worker is marked for a quiet wind-down: a quiet window (20 seconds by default) — and the daemon closes the session, waking no one. If the worker stays silent and doesn't reply, a safety bound (600 seconds by default) winds it down anyway.
67
+ - **`iapeer self-done`** — a self-call by a worker that has nothing to send: it frees the queue for the next task without waking anyone. The doctrine is "nothing to send — `self-done` instead of an empty reply".
68
+
69
+ This model keeps a stream of one-off tasks orderly: one worker per task, with no accumulation of parallel sessions and no empty wakes of the recipient.
@@ -0,0 +1,60 @@
1
+ # 11 — Extensions and memory
2
+
3
+ [Русский](ru/11-расширения-и-память.md) · **English**
4
+
5
+ A peer's capabilities are extended by two kinds of package: capability plugins (abilities — tools, context) and runtime packages (modes of presence). Memory is a separate mechanism: one provider per host, through a slot. This section is how to use these; the contract for package developers is in [13 — Architecture and contract](13-architecture-and-contract.md).
6
+
7
+ ## Capability plugins: enable
8
+
9
+ A plugin adds an ability to a peer without being a runtime — shared memory, speech synthesis, a set of skills. Attaching it:
10
+
11
+ ```bash
12
+ iapeer enable peer-voice assistant # attach a plugin to a peer
13
+ iapeer enable iapeer-memory # to the current folder's peer
14
+ iapeer enable peer-voice assistant --no-setup # without the plugin's setup step
15
+ ```
16
+
17
+ The command installs the plugin on each of the peer's agent runtimes (for Claude — into the project scope inside the peer's folder, for Codex — into the host's shared config), enables it, and, if the plugin declares a setup step, runs it. The operation is idempotent and doesn't disturb other peers: for Claude the binding is by the specific peer's folder.
18
+
19
+ A runtime's local marketplace snapshot may lag: a plugin registered in the marketplace after the host's last sync reads as an "unknown plugin" until then. So if the install doesn't go through the first time, `enable` refreshes the runtime's marketplace snapshot (Claude — `marketplace update`, Codex — `marketplace upgrade`) and retries once.
20
+
21
+ ## The memory slot
22
+
23
+ Memory in iapeer is a first-class option, not a built-in feature. It's provided by a **memory provider** — a separate package that claims the single per-host slot. The slot is either occupied by exactly one provider or empty; an empty slot is a normal state.
24
+
25
+ The default provider is installed at onboarding:
26
+
27
+ ```bash
28
+ iapeer onboard # installs the default provider if the slot is empty
29
+ iapeer onboard --memory <package> # specify a different provider
30
+ iapeer onboard --no-memory # leave the slot empty
31
+ ```
32
+
33
+ The slot state is shown by `iapeer status` — the provider name, version, and the freshness of its daemon (or `none` if the slot is empty).
34
+
35
+ The reference provider is **iapeer-memory** (`@agfpd/iapeer-memory`), also the default. It's the team's shared memory: a knowledge canon (knowledge, decisions, ideas, projects, lists) and each agent's personal operative memory, with search over content and the link graph. Memory reaches an agent two ways — a map of its notes lands in the system prompt as a fragment layer ([05 — System prompt](05-system-prompt.md)), and it reads and writes through the provider's MCP tools. Details — in iapeer-memory's own documentation.
36
+
37
+ When the slot is claimed, the core calls the provider's commands at a peer's life events: at peer birth (`birth`) — to set up its memory, at removal (`remove`) — to wind it down. The provider manifest format, the commands, and the call moments — in [13](13-architecture-and-contract.md).
38
+
39
+ ### A runtime's native memory
40
+
41
+ Claude and Codex have their own built-in memory. When the slot is occupied by a provider, keeping the runtime's native memory in parallel is harmful — a second, uncurated set accumulates. So at the birth of a peer with a claimed slot, the core automatically turns off its runtimes' native memory.
42
+
43
+ To manage this by hand:
44
+
45
+ ```bash
46
+ iapeer native-memory off --peer assistant # turn off native memory
47
+ iapeer native-memory on --peer assistant # restore it
48
+ iapeer native-memory off --all # across all peers
49
+ ```
50
+
51
+ ## Runtime packages
52
+
53
+ Modes of presence (Telegram, the scheduler) ship as runtime packages:
54
+
55
+ ```bash
56
+ iapeer install-runtime notifier # install + provision the declared peers
57
+ iapeer update-runtime --all # update all runtime packages
58
+ ```
59
+
60
+ `install-runtime` installs the package and provisions the peer set it declares (notifier — `timer` and `watcher`). A runtime with no declared set (telegram) has its peers set up by the operator with `iapeer create … --runtime telegram`. Telegram connection details — [09 — Runtimes](09-runtimes.md); the runtime package contract — [13](13-architecture-and-contract.md).