inter-agent-pi 0.2.0__tar.gz
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.
- inter_agent_pi-0.2.0/LICENSE.md +21 -0
- inter_agent_pi-0.2.0/PKG-INFO +228 -0
- inter_agent_pi-0.2.0/README.md +210 -0
- inter_agent_pi-0.2.0/pyproject.toml +61 -0
- inter_agent_pi-0.2.0/setup.cfg +4 -0
- inter_agent_pi-0.2.0/src/inter_agent_pi/__init__.py +1 -0
- inter_agent_pi-0.2.0/src/inter_agent_pi/cli.py +88 -0
- inter_agent_pi-0.2.0/src/inter_agent_pi/commands.py +348 -0
- inter_agent_pi-0.2.0/src/inter_agent_pi/listener.py +409 -0
- inter_agent_pi-0.2.0/src/inter_agent_pi.egg-info/PKG-INFO +228 -0
- inter_agent_pi-0.2.0/src/inter_agent_pi.egg-info/SOURCES.txt +17 -0
- inter_agent_pi-0.2.0/src/inter_agent_pi.egg-info/dependency_links.txt +1 -0
- inter_agent_pi-0.2.0/src/inter_agent_pi.egg-info/entry_points.txt +2 -0
- inter_agent_pi-0.2.0/src/inter_agent_pi.egg-info/requires.txt +2 -0
- inter_agent_pi-0.2.0/src/inter_agent_pi.egg-info/top_level.txt +1 -0
- inter_agent_pi-0.2.0/tests/test_console_entry_points.py +26 -0
- inter_agent_pi-0.2.0/tests/test_pi_adapter_cli.py +694 -0
- inter_agent_pi-0.2.0/tests/test_pi_extension_static.py +919 -0
- inter_agent_pi-0.2.0/tests/test_pi_listener.py +614 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 arcanemachine
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: inter-agent-pi
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Pi helper for the inter-agent message bus: connect listener and command-line tools
|
|
5
|
+
Classifier: Development Status :: 3 - Alpha
|
|
6
|
+
Classifier: Intended Audience :: Developers
|
|
7
|
+
Classifier: Programming Language :: Python :: 3
|
|
8
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
9
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
11
|
+
Classifier: Topic :: Communications
|
|
12
|
+
Requires-Python: >=3.10
|
|
13
|
+
Description-Content-Type: text/markdown
|
|
14
|
+
License-File: LICENSE.md
|
|
15
|
+
Requires-Dist: inter-agent-core==0.2.0
|
|
16
|
+
Requires-Dist: websockets==16.0
|
|
17
|
+
Dynamic: license-file
|
|
18
|
+
|
|
19
|
+
# inter-agent-pi
|
|
20
|
+
|
|
21
|
+
[](https://github.com/arcanemachine/inter-agent-pi)
|
|
22
|
+
|
|
23
|
+
The Pi extension for the inter-agent message bus.
|
|
24
|
+
|
|
25
|
+
It connects a Pi coding-agent session to the bus as a named agent,
|
|
26
|
+
exposes grouped `/inter-agent` commands and a bounded set of agent-callable
|
|
27
|
+
tools, delivers incoming peer messages as Pi notifications, and ships a Python
|
|
28
|
+
helper (`inter-agent-pi` console command, `inter_agent_pi` import package) that
|
|
29
|
+
wraps the importable [`inter-agent-core`](#runtime-dependency) listener and
|
|
30
|
+
command APIs.
|
|
31
|
+
|
|
32
|
+
This repository is an independent clean-history child. It contains no former
|
|
33
|
+
monorepo history, no private workflow, no core runtime source, and no Claude
|
|
34
|
+
Code material.
|
|
35
|
+
|
|
36
|
+
## Installation (Pi extension)
|
|
37
|
+
|
|
38
|
+
From a Pi session:
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
pi install npm:inter-agent-pi@0.2.0
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Or install from a published Git tag (once published; do not pin a raw commit
|
|
45
|
+
hash):
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
pi install https://github.com/arcanemachine/inter-agent-pi
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Or load directly from a source checkout during development:
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
pi -e /path/to/inter-agent-pi/src/index.ts
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
The extension entry point is `./src/index.ts`.
|
|
58
|
+
|
|
59
|
+
## Runtime dependency
|
|
60
|
+
|
|
61
|
+
The helper depends on `inter-agent-core` `0.2.0` and on `websockets` `16.0`. A compatible `inter-agent-core` install
|
|
62
|
+
must provide the `inter-agent-pi`, `inter-agent-server`, `inter-agent-connect`,
|
|
63
|
+
`inter-agent-send`, `inter-agent-list`, `inter-agent-status`,
|
|
64
|
+
`inter-agent-shutdown`, `inter-agent-kick`, `inter-agent-publish`, and
|
|
65
|
+
`inter-agent-channels` console commands and the importable `inter_agent`
|
|
66
|
+
namespace, including the promoted `inter_agent.core.adapter_control` bridge.
|
|
67
|
+
|
|
68
|
+
> **Development note (non-release):** while the permanent `inter-agent-core`
|
|
69
|
+
> repository is being prepared, the Python helper may be resolved against a
|
|
70
|
+
> temporary local `inter-agent-core` candidate via a migration-only
|
|
71
|
+
> `[tool.uv.sources]` path entry. That path source is removed and the lock is
|
|
72
|
+
> re-resolved against the permanent `inter-agent-core` repository before any
|
|
73
|
+
> publication. Never publish while the temporary path source remains.
|
|
74
|
+
|
|
75
|
+
### Helper resolution precedence
|
|
76
|
+
|
|
77
|
+
The extension resolves the Python runtime in this order:
|
|
78
|
+
|
|
79
|
+
1. `INTER_AGENT_PI_HELPER` — an exact path to an `inter-agent-pi` executable;
|
|
80
|
+
its bin directory must also contain the required core helper scripts.
|
|
81
|
+
2. An explicitly configured `interAgent.projectPath` — the helper is resolved
|
|
82
|
+
from that checkout's `.venv/bin`; if it is configured but incomplete the
|
|
83
|
+
extension fails fast with a bounded, actionable message.
|
|
84
|
+
3. The extension-managed, documented runtime venv.
|
|
85
|
+
4. `inter-agent-pi`, `inter-agent-connect`, and `inter-agent-server` discovered
|
|
86
|
+
together on `PATH`.
|
|
87
|
+
5. A bounded setup-needed failure pointing back to this README.
|
|
88
|
+
|
|
89
|
+
The legacy implicit fallback to `~/.local/share/inter-agent` is intentionally
|
|
90
|
+
removed; that was a monorepo-era bootstrap assumption that no longer applies
|
|
91
|
+
to the standalone package. Explicitly configured `interAgent.projectPath`
|
|
92
|
+
remains supported for development.
|
|
93
|
+
|
|
94
|
+
## Installing the Python helper
|
|
95
|
+
|
|
96
|
+
From a source checkout:
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
uv sync --locked # resolve inter-agent-core + dev/test tooling
|
|
100
|
+
uv build # build wheel + sdist into dist/
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Install the built wheel into a venv that already provides a compatible
|
|
104
|
+
`inter-agent-core`, for example:
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
uv venv .venv
|
|
108
|
+
uv pip install ./dist/*.whl <compatible-inter-agent-core-wheel>
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
The `inter-agent-pi` console command and the agent-callable tools all reuse
|
|
112
|
+
this runtime.
|
|
113
|
+
|
|
114
|
+
## Configuration
|
|
115
|
+
|
|
116
|
+
Configuration is read from the Pi agent settings file under `interAgent.*`.
|
|
117
|
+
Relative paths (`projectPath`, `dataDir`, TLS cert/key) resolve against the
|
|
118
|
+
directory of the settings file.
|
|
119
|
+
|
|
120
|
+
- `host` / `port` — override the default bus endpoint.
|
|
121
|
+
- `dataDir` — shared bus state directory; defaults to the core default.
|
|
122
|
+
- `secret` — shared secret for challenge-response auth; forwarded to helpers
|
|
123
|
+
as `INTER_AGENT_SECRET`. Do not store secrets in plaintext files in the repo.
|
|
124
|
+
- `tls` / `tlsCert` / `tlsKey` — enable explicit TLS and point at a
|
|
125
|
+
certificate/key pair.
|
|
126
|
+
|
|
127
|
+
Helper resolution precedence and the environment variables used by the helper
|
|
128
|
+
match [`inter-agent-core`](#runtime-dependency). Helper install path, runtime
|
|
129
|
+
state/config path, and bus state directories stay distinct so the bus identity
|
|
130
|
+
does not fragment across installs.
|
|
131
|
+
|
|
132
|
+
## Commands
|
|
133
|
+
|
|
134
|
+
All commands ride the grouped `/inter-agent` command:
|
|
135
|
+
|
|
136
|
+
```
|
|
137
|
+
usage: /inter-agent <connect|disconnect|kick|rename|send|broadcast|publish|channels|subscribe|unsubscribe|list|status|delivery> [args]
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
- `/inter-agent connect <name> [--label <label>]` — connect this session as a
|
|
141
|
+
named agent; auto-starts the server if unavailable.
|
|
142
|
+
- `/inter-agent disconnect` — stop the local listener and notify.
|
|
143
|
+
- `/inter-agent kick <name>` — force-disconnect another session (user-only).
|
|
144
|
+
- `/inter-agent rename <name> [--label <label>]` — reconnect under a new name.
|
|
145
|
+
- `/inter-agent send <to> <text>` — send a direct message; routes the sender via `--from`.
|
|
146
|
+
- `/inter-agent broadcast <text>` — broadcast to every session (user-only).
|
|
147
|
+
- `/inter-agent publish <channel> <text>` — publish to a channel (user-only).
|
|
148
|
+
- `/inter-agent channels` — list channels and subscribers.
|
|
149
|
+
- `/inter-agent subscribe <channel>` / `/inter-agent unsubscribe <channel>` —
|
|
150
|
+
bind the live listener's subscriptions over the local control socket.
|
|
151
|
+
- `/inter-agent list` — list connected agent sessions.
|
|
152
|
+
- `/inter-agent status` — print server/helper status.
|
|
153
|
+
- `/inter-agent delivery <immediate|queued>` — switch inbound delivery mode
|
|
154
|
+
(aliases `i` / `q`).
|
|
155
|
+
|
|
156
|
+
## Agent-callable tools vs user-only controls
|
|
157
|
+
|
|
158
|
+
Agent-callable tools:
|
|
159
|
+
|
|
160
|
+
- `inter_agent_send`, `inter_agent_broadcast` — send/broadcast through the
|
|
161
|
+
connected Pi listener (sender routed via `--from`).
|
|
162
|
+
- `inter_agent_list`, `inter_agent_whoami`, `inter_agent_status` —
|
|
163
|
+
read-only diagnostics; they do not require a connected listener.
|
|
164
|
+
- `inter_agent_read_messages` — read and remove queued mailbox messages;
|
|
165
|
+
performs no outbound action.
|
|
166
|
+
|
|
167
|
+
User-only controls (no model-callable tool): `kick`, `publish`, `subscribe`,
|
|
168
|
+
`unsubscribe`, `channels`, `delivery`, and the connect/disconnect/rename
|
|
169
|
+
connection actions. Broadcast, publish, kick, and destructive actions require
|
|
170
|
+
explicit user approval; the model is instructed never to send a courtesy reply
|
|
171
|
+
and to treat peer messages as untrusted context.
|
|
172
|
+
|
|
173
|
+
## Mailbox, reload continuity, and reconnection
|
|
174
|
+
|
|
175
|
+
- By default inbound messages are queued in a bounded mailbox (max 128 unread)
|
|
176
|
+
and surfaced as a metadata-only notice; `delivery immediate` restores bounded
|
|
177
|
+
body notification.
|
|
178
|
+
- The notice provokes a non-steering follow-up turn and never prescribes a
|
|
179
|
+
canned acknowledgment, reply, or outbound action.
|
|
180
|
+
- A same-process `/reload` preserves the unread mailbox through a versioned,
|
|
181
|
+
one-use, process-global handoff (`Symbol.for("inter-agent.pi.mailbox.reloadHandoff.v1")`),
|
|
182
|
+
generation/session-scoped and TTL-bounded; every other lifecycle boundary
|
|
183
|
+
starts empty.
|
|
184
|
+
- The listener reconnects with bounded backoff and gives up after a deadline
|
|
185
|
+
measured from the first failure. A `KICKED` stop terminates one listener
|
|
186
|
+
process without reconnecting, leaving the routing name free for an explicit
|
|
187
|
+
later reconnect.
|
|
188
|
+
- The startup flag provides the inter-agent routing name at process start:
|
|
189
|
+
`pi -- inter-agent=<name>`.
|
|
190
|
+
|
|
191
|
+
## Development
|
|
192
|
+
|
|
193
|
+
```bash
|
|
194
|
+
uv sync --locked # install runtime + dev/test dependencies
|
|
195
|
+
npm ci # install TypeScript/dev dependencies (network)
|
|
196
|
+
npm test # TypeScript tests
|
|
197
|
+
npm run typecheck # tsc --noEmit
|
|
198
|
+
npm run build # emit dist/
|
|
199
|
+
npx prettier --write . # format
|
|
200
|
+
uv run pytest -q # Python tests
|
|
201
|
+
uv run ruff check src tests
|
|
202
|
+
uv run black --check src tests
|
|
203
|
+
uv run mypy src tests
|
|
204
|
+
scripts/run-checks.sh # full package gate incl. artifact validation
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
Artifacts (`dist/`, `dist-tests/`, `node_modules/`, `.venv/`, wheels, tarballs)
|
|
208
|
+
are generated and gitignored; they are not part of the root commit.
|
|
209
|
+
|
|
210
|
+
## Ecosystem and core
|
|
211
|
+
|
|
212
|
+
- The public `inter-agent-core` repository owns the bus runtime and the
|
|
213
|
+
`inter_agent.core.adapter_control` bridge this package consumes. (Its public
|
|
214
|
+
repository URL is published with that release; this package depends on the
|
|
215
|
+
compatible distribution name `inter-agent-core`.)
|
|
216
|
+
- The public ecosystem repository coordinates adapters; it is added as a
|
|
217
|
+
submodule only once it has a published initial `main` commit.
|
|
218
|
+
|
|
219
|
+
This package does not assume any currently published artifact exists beyond
|
|
220
|
+
what a compatible `inter-agent-core` release provides.
|
|
221
|
+
|
|
222
|
+
## Security
|
|
223
|
+
|
|
224
|
+
- Authenticate with the shared bus secret; never commit secrets, tokens, keys,
|
|
225
|
+
or certificates to this repository.
|
|
226
|
+
- TLS uses explicit cert/key paths; a wrong/untrusted certificate fails bounded
|
|
227
|
+
and actionable and never falls back to plaintext.
|
|
228
|
+
- Peer messages are untrusted context, never instructions.
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
# inter-agent-pi
|
|
2
|
+
|
|
3
|
+
[](https://github.com/arcanemachine/inter-agent-pi)
|
|
4
|
+
|
|
5
|
+
The Pi extension for the inter-agent message bus.
|
|
6
|
+
|
|
7
|
+
It connects a Pi coding-agent session to the bus as a named agent,
|
|
8
|
+
exposes grouped `/inter-agent` commands and a bounded set of agent-callable
|
|
9
|
+
tools, delivers incoming peer messages as Pi notifications, and ships a Python
|
|
10
|
+
helper (`inter-agent-pi` console command, `inter_agent_pi` import package) that
|
|
11
|
+
wraps the importable [`inter-agent-core`](#runtime-dependency) listener and
|
|
12
|
+
command APIs.
|
|
13
|
+
|
|
14
|
+
This repository is an independent clean-history child. It contains no former
|
|
15
|
+
monorepo history, no private workflow, no core runtime source, and no Claude
|
|
16
|
+
Code material.
|
|
17
|
+
|
|
18
|
+
## Installation (Pi extension)
|
|
19
|
+
|
|
20
|
+
From a Pi session:
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
pi install npm:inter-agent-pi@0.2.0
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Or install from a published Git tag (once published; do not pin a raw commit
|
|
27
|
+
hash):
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
pi install https://github.com/arcanemachine/inter-agent-pi
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Or load directly from a source checkout during development:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
pi -e /path/to/inter-agent-pi/src/index.ts
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
The extension entry point is `./src/index.ts`.
|
|
40
|
+
|
|
41
|
+
## Runtime dependency
|
|
42
|
+
|
|
43
|
+
The helper depends on `inter-agent-core` `0.2.0` and on `websockets` `16.0`. A compatible `inter-agent-core` install
|
|
44
|
+
must provide the `inter-agent-pi`, `inter-agent-server`, `inter-agent-connect`,
|
|
45
|
+
`inter-agent-send`, `inter-agent-list`, `inter-agent-status`,
|
|
46
|
+
`inter-agent-shutdown`, `inter-agent-kick`, `inter-agent-publish`, and
|
|
47
|
+
`inter-agent-channels` console commands and the importable `inter_agent`
|
|
48
|
+
namespace, including the promoted `inter_agent.core.adapter_control` bridge.
|
|
49
|
+
|
|
50
|
+
> **Development note (non-release):** while the permanent `inter-agent-core`
|
|
51
|
+
> repository is being prepared, the Python helper may be resolved against a
|
|
52
|
+
> temporary local `inter-agent-core` candidate via a migration-only
|
|
53
|
+
> `[tool.uv.sources]` path entry. That path source is removed and the lock is
|
|
54
|
+
> re-resolved against the permanent `inter-agent-core` repository before any
|
|
55
|
+
> publication. Never publish while the temporary path source remains.
|
|
56
|
+
|
|
57
|
+
### Helper resolution precedence
|
|
58
|
+
|
|
59
|
+
The extension resolves the Python runtime in this order:
|
|
60
|
+
|
|
61
|
+
1. `INTER_AGENT_PI_HELPER` — an exact path to an `inter-agent-pi` executable;
|
|
62
|
+
its bin directory must also contain the required core helper scripts.
|
|
63
|
+
2. An explicitly configured `interAgent.projectPath` — the helper is resolved
|
|
64
|
+
from that checkout's `.venv/bin`; if it is configured but incomplete the
|
|
65
|
+
extension fails fast with a bounded, actionable message.
|
|
66
|
+
3. The extension-managed, documented runtime venv.
|
|
67
|
+
4. `inter-agent-pi`, `inter-agent-connect`, and `inter-agent-server` discovered
|
|
68
|
+
together on `PATH`.
|
|
69
|
+
5. A bounded setup-needed failure pointing back to this README.
|
|
70
|
+
|
|
71
|
+
The legacy implicit fallback to `~/.local/share/inter-agent` is intentionally
|
|
72
|
+
removed; that was a monorepo-era bootstrap assumption that no longer applies
|
|
73
|
+
to the standalone package. Explicitly configured `interAgent.projectPath`
|
|
74
|
+
remains supported for development.
|
|
75
|
+
|
|
76
|
+
## Installing the Python helper
|
|
77
|
+
|
|
78
|
+
From a source checkout:
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
uv sync --locked # resolve inter-agent-core + dev/test tooling
|
|
82
|
+
uv build # build wheel + sdist into dist/
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Install the built wheel into a venv that already provides a compatible
|
|
86
|
+
`inter-agent-core`, for example:
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
uv venv .venv
|
|
90
|
+
uv pip install ./dist/*.whl <compatible-inter-agent-core-wheel>
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
The `inter-agent-pi` console command and the agent-callable tools all reuse
|
|
94
|
+
this runtime.
|
|
95
|
+
|
|
96
|
+
## Configuration
|
|
97
|
+
|
|
98
|
+
Configuration is read from the Pi agent settings file under `interAgent.*`.
|
|
99
|
+
Relative paths (`projectPath`, `dataDir`, TLS cert/key) resolve against the
|
|
100
|
+
directory of the settings file.
|
|
101
|
+
|
|
102
|
+
- `host` / `port` — override the default bus endpoint.
|
|
103
|
+
- `dataDir` — shared bus state directory; defaults to the core default.
|
|
104
|
+
- `secret` — shared secret for challenge-response auth; forwarded to helpers
|
|
105
|
+
as `INTER_AGENT_SECRET`. Do not store secrets in plaintext files in the repo.
|
|
106
|
+
- `tls` / `tlsCert` / `tlsKey` — enable explicit TLS and point at a
|
|
107
|
+
certificate/key pair.
|
|
108
|
+
|
|
109
|
+
Helper resolution precedence and the environment variables used by the helper
|
|
110
|
+
match [`inter-agent-core`](#runtime-dependency). Helper install path, runtime
|
|
111
|
+
state/config path, and bus state directories stay distinct so the bus identity
|
|
112
|
+
does not fragment across installs.
|
|
113
|
+
|
|
114
|
+
## Commands
|
|
115
|
+
|
|
116
|
+
All commands ride the grouped `/inter-agent` command:
|
|
117
|
+
|
|
118
|
+
```
|
|
119
|
+
usage: /inter-agent <connect|disconnect|kick|rename|send|broadcast|publish|channels|subscribe|unsubscribe|list|status|delivery> [args]
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
- `/inter-agent connect <name> [--label <label>]` — connect this session as a
|
|
123
|
+
named agent; auto-starts the server if unavailable.
|
|
124
|
+
- `/inter-agent disconnect` — stop the local listener and notify.
|
|
125
|
+
- `/inter-agent kick <name>` — force-disconnect another session (user-only).
|
|
126
|
+
- `/inter-agent rename <name> [--label <label>]` — reconnect under a new name.
|
|
127
|
+
- `/inter-agent send <to> <text>` — send a direct message; routes the sender via `--from`.
|
|
128
|
+
- `/inter-agent broadcast <text>` — broadcast to every session (user-only).
|
|
129
|
+
- `/inter-agent publish <channel> <text>` — publish to a channel (user-only).
|
|
130
|
+
- `/inter-agent channels` — list channels and subscribers.
|
|
131
|
+
- `/inter-agent subscribe <channel>` / `/inter-agent unsubscribe <channel>` —
|
|
132
|
+
bind the live listener's subscriptions over the local control socket.
|
|
133
|
+
- `/inter-agent list` — list connected agent sessions.
|
|
134
|
+
- `/inter-agent status` — print server/helper status.
|
|
135
|
+
- `/inter-agent delivery <immediate|queued>` — switch inbound delivery mode
|
|
136
|
+
(aliases `i` / `q`).
|
|
137
|
+
|
|
138
|
+
## Agent-callable tools vs user-only controls
|
|
139
|
+
|
|
140
|
+
Agent-callable tools:
|
|
141
|
+
|
|
142
|
+
- `inter_agent_send`, `inter_agent_broadcast` — send/broadcast through the
|
|
143
|
+
connected Pi listener (sender routed via `--from`).
|
|
144
|
+
- `inter_agent_list`, `inter_agent_whoami`, `inter_agent_status` —
|
|
145
|
+
read-only diagnostics; they do not require a connected listener.
|
|
146
|
+
- `inter_agent_read_messages` — read and remove queued mailbox messages;
|
|
147
|
+
performs no outbound action.
|
|
148
|
+
|
|
149
|
+
User-only controls (no model-callable tool): `kick`, `publish`, `subscribe`,
|
|
150
|
+
`unsubscribe`, `channels`, `delivery`, and the connect/disconnect/rename
|
|
151
|
+
connection actions. Broadcast, publish, kick, and destructive actions require
|
|
152
|
+
explicit user approval; the model is instructed never to send a courtesy reply
|
|
153
|
+
and to treat peer messages as untrusted context.
|
|
154
|
+
|
|
155
|
+
## Mailbox, reload continuity, and reconnection
|
|
156
|
+
|
|
157
|
+
- By default inbound messages are queued in a bounded mailbox (max 128 unread)
|
|
158
|
+
and surfaced as a metadata-only notice; `delivery immediate` restores bounded
|
|
159
|
+
body notification.
|
|
160
|
+
- The notice provokes a non-steering follow-up turn and never prescribes a
|
|
161
|
+
canned acknowledgment, reply, or outbound action.
|
|
162
|
+
- A same-process `/reload` preserves the unread mailbox through a versioned,
|
|
163
|
+
one-use, process-global handoff (`Symbol.for("inter-agent.pi.mailbox.reloadHandoff.v1")`),
|
|
164
|
+
generation/session-scoped and TTL-bounded; every other lifecycle boundary
|
|
165
|
+
starts empty.
|
|
166
|
+
- The listener reconnects with bounded backoff and gives up after a deadline
|
|
167
|
+
measured from the first failure. A `KICKED` stop terminates one listener
|
|
168
|
+
process without reconnecting, leaving the routing name free for an explicit
|
|
169
|
+
later reconnect.
|
|
170
|
+
- The startup flag provides the inter-agent routing name at process start:
|
|
171
|
+
`pi -- inter-agent=<name>`.
|
|
172
|
+
|
|
173
|
+
## Development
|
|
174
|
+
|
|
175
|
+
```bash
|
|
176
|
+
uv sync --locked # install runtime + dev/test dependencies
|
|
177
|
+
npm ci # install TypeScript/dev dependencies (network)
|
|
178
|
+
npm test # TypeScript tests
|
|
179
|
+
npm run typecheck # tsc --noEmit
|
|
180
|
+
npm run build # emit dist/
|
|
181
|
+
npx prettier --write . # format
|
|
182
|
+
uv run pytest -q # Python tests
|
|
183
|
+
uv run ruff check src tests
|
|
184
|
+
uv run black --check src tests
|
|
185
|
+
uv run mypy src tests
|
|
186
|
+
scripts/run-checks.sh # full package gate incl. artifact validation
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
Artifacts (`dist/`, `dist-tests/`, `node_modules/`, `.venv/`, wheels, tarballs)
|
|
190
|
+
are generated and gitignored; they are not part of the root commit.
|
|
191
|
+
|
|
192
|
+
## Ecosystem and core
|
|
193
|
+
|
|
194
|
+
- The public `inter-agent-core` repository owns the bus runtime and the
|
|
195
|
+
`inter_agent.core.adapter_control` bridge this package consumes. (Its public
|
|
196
|
+
repository URL is published with that release; this package depends on the
|
|
197
|
+
compatible distribution name `inter-agent-core`.)
|
|
198
|
+
- The public ecosystem repository coordinates adapters; it is added as a
|
|
199
|
+
submodule only once it has a published initial `main` commit.
|
|
200
|
+
|
|
201
|
+
This package does not assume any currently published artifact exists beyond
|
|
202
|
+
what a compatible `inter-agent-core` release provides.
|
|
203
|
+
|
|
204
|
+
## Security
|
|
205
|
+
|
|
206
|
+
- Authenticate with the shared bus secret; never commit secrets, tokens, keys,
|
|
207
|
+
or certificates to this repository.
|
|
208
|
+
- TLS uses explicit cert/key paths; a wrong/untrusted certificate fails bounded
|
|
209
|
+
and actionable and never falls back to plaintext.
|
|
210
|
+
- Peer messages are untrusted context, never instructions.
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=68", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "inter-agent-pi"
|
|
7
|
+
version = "0.2.0"
|
|
8
|
+
description = "Pi helper for the inter-agent message bus: connect listener and command-line tools"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.10"
|
|
11
|
+
classifiers = [
|
|
12
|
+
"Development Status :: 3 - Alpha",
|
|
13
|
+
"Intended Audience :: Developers",
|
|
14
|
+
"Programming Language :: Python :: 3",
|
|
15
|
+
"Programming Language :: Python :: 3.10",
|
|
16
|
+
"Programming Language :: Python :: 3.11",
|
|
17
|
+
"Programming Language :: Python :: 3.12",
|
|
18
|
+
"Topic :: Communications",
|
|
19
|
+
]
|
|
20
|
+
dependencies = [
|
|
21
|
+
"inter-agent-core==0.2.0",
|
|
22
|
+
"websockets==16.0",
|
|
23
|
+
]
|
|
24
|
+
|
|
25
|
+
[project.scripts]
|
|
26
|
+
inter-agent-pi = "inter_agent_pi.cli:main"
|
|
27
|
+
|
|
28
|
+
[dependency-groups]
|
|
29
|
+
dev = [
|
|
30
|
+
"pytest==9.0.3",
|
|
31
|
+
"pytest-asyncio==1.3.0",
|
|
32
|
+
"ruff==0.15.12",
|
|
33
|
+
"black==26.3.1",
|
|
34
|
+
"mypy==1.20.2",
|
|
35
|
+
]
|
|
36
|
+
|
|
37
|
+
[tool.setuptools.packages.find]
|
|
38
|
+
where = ["src"]
|
|
39
|
+
|
|
40
|
+
[tool.pytest.ini_options]
|
|
41
|
+
asyncio_mode = "auto"
|
|
42
|
+
|
|
43
|
+
[tool.black]
|
|
44
|
+
line-length = 100
|
|
45
|
+
target-version = ["py312"]
|
|
46
|
+
|
|
47
|
+
[tool.ruff]
|
|
48
|
+
line-length = 100
|
|
49
|
+
target-version = "py312"
|
|
50
|
+
|
|
51
|
+
[tool.ruff.lint]
|
|
52
|
+
select = ["E", "F", "I", "B", "UP"]
|
|
53
|
+
|
|
54
|
+
[tool.mypy]
|
|
55
|
+
python_version = "3.12"
|
|
56
|
+
strict = true
|
|
57
|
+
ignore_missing_imports = true
|
|
58
|
+
|
|
59
|
+
[[tool.mypy.overrides]]
|
|
60
|
+
module = ["tests.*"]
|
|
61
|
+
disallow_untyped_defs = false
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Pi adapter package."""
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import argparse
|
|
4
|
+
from collections.abc import Sequence
|
|
5
|
+
|
|
6
|
+
from inter_agent_pi import commands
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def build_parser() -> argparse.ArgumentParser:
|
|
10
|
+
parser = argparse.ArgumentParser(prog="inter-agent-pi")
|
|
11
|
+
sub = parser.add_subparsers(dest="command", required=True)
|
|
12
|
+
|
|
13
|
+
connect = sub.add_parser("connect")
|
|
14
|
+
connect.add_argument("name")
|
|
15
|
+
connect.add_argument("--label")
|
|
16
|
+
|
|
17
|
+
send = sub.add_parser("send")
|
|
18
|
+
send.add_argument("to")
|
|
19
|
+
send.add_argument("text")
|
|
20
|
+
send.add_argument("--from", dest="from_name")
|
|
21
|
+
|
|
22
|
+
broadcast = sub.add_parser("broadcast")
|
|
23
|
+
broadcast.add_argument("text")
|
|
24
|
+
broadcast.add_argument("--from", dest="from_name")
|
|
25
|
+
|
|
26
|
+
subscribe = sub.add_parser("subscribe")
|
|
27
|
+
subscribe.add_argument("channel")
|
|
28
|
+
subscribe.add_argument("--name", required=True)
|
|
29
|
+
|
|
30
|
+
unsubscribe = sub.add_parser("unsubscribe")
|
|
31
|
+
unsubscribe.add_argument("channel")
|
|
32
|
+
unsubscribe.add_argument("--name", required=True)
|
|
33
|
+
|
|
34
|
+
kick = sub.add_parser("kick")
|
|
35
|
+
kick.add_argument("name")
|
|
36
|
+
|
|
37
|
+
publish = sub.add_parser("publish")
|
|
38
|
+
publish.add_argument("channel")
|
|
39
|
+
publish.add_argument("text")
|
|
40
|
+
publish.add_argument("--from", dest="from_name")
|
|
41
|
+
|
|
42
|
+
channels = sub.add_parser("channels")
|
|
43
|
+
channels.add_argument("--json", action="store_true", help="emit JSON protocol output")
|
|
44
|
+
|
|
45
|
+
list_parser = sub.add_parser("list")
|
|
46
|
+
list_parser.add_argument("--json", action="store_true", help="emit JSON protocol output")
|
|
47
|
+
|
|
48
|
+
status = sub.add_parser("status")
|
|
49
|
+
status.add_argument("--json", action="store_true", help="emit JSON status output")
|
|
50
|
+
|
|
51
|
+
sub.add_parser("shutdown")
|
|
52
|
+
return parser
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def main(argv: Sequence[str] | None = None) -> int:
|
|
56
|
+
parser = build_parser()
|
|
57
|
+
args = parser.parse_args(argv)
|
|
58
|
+
|
|
59
|
+
if args.command == "connect":
|
|
60
|
+
return commands.connect(args.name, args.label)
|
|
61
|
+
if args.command == "send":
|
|
62
|
+
return commands.send(args.to, args.text, args.from_name)
|
|
63
|
+
if args.command == "broadcast":
|
|
64
|
+
return commands.broadcast(args.text, args.from_name)
|
|
65
|
+
if args.command == "subscribe":
|
|
66
|
+
return commands.subscribe(args.channel, args.name)
|
|
67
|
+
if args.command == "unsubscribe":
|
|
68
|
+
return commands.unsubscribe(args.channel, args.name)
|
|
69
|
+
if args.command == "kick":
|
|
70
|
+
return commands.kick(args.name)
|
|
71
|
+
if args.command == "publish":
|
|
72
|
+
return commands.publish(args.channel, args.text, args.from_name)
|
|
73
|
+
if args.command == "channels":
|
|
74
|
+
return commands.channels(as_json=args.json)
|
|
75
|
+
if args.command == "list":
|
|
76
|
+
return commands.list_sessions()
|
|
77
|
+
if args.command == "status":
|
|
78
|
+
print(commands.status_json())
|
|
79
|
+
return 0
|
|
80
|
+
if args.command == "shutdown":
|
|
81
|
+
return commands.shutdown()
|
|
82
|
+
|
|
83
|
+
parser.print_help()
|
|
84
|
+
return 2
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
if __name__ == "__main__":
|
|
88
|
+
raise SystemExit(main())
|