agent-inbox 0.0.1 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CLAUDE.md +113 -0
- package/README.md +195 -1
- package/dist/federation/address.d.ts +24 -0
- package/dist/federation/address.d.ts.map +1 -0
- package/dist/federation/address.js +54 -0
- package/dist/federation/address.js.map +1 -0
- package/dist/federation/connection-manager.d.ts +118 -0
- package/dist/federation/connection-manager.d.ts.map +1 -0
- package/dist/federation/connection-manager.js +369 -0
- package/dist/federation/connection-manager.js.map +1 -0
- package/dist/federation/delivery-queue.d.ts +66 -0
- package/dist/federation/delivery-queue.d.ts.map +1 -0
- package/dist/federation/delivery-queue.js +199 -0
- package/dist/federation/delivery-queue.js.map +1 -0
- package/dist/federation/index.d.ts +7 -0
- package/dist/federation/index.d.ts.map +1 -0
- package/dist/federation/index.js +6 -0
- package/dist/federation/index.js.map +1 -0
- package/dist/federation/routing-engine.d.ts +74 -0
- package/dist/federation/routing-engine.d.ts.map +1 -0
- package/dist/federation/routing-engine.js +158 -0
- package/dist/federation/routing-engine.js.map +1 -0
- package/dist/federation/trust.d.ts +39 -0
- package/dist/federation/trust.d.ts.map +1 -0
- package/dist/federation/trust.js +64 -0
- package/dist/federation/trust.js.map +1 -0
- package/dist/index.d.ts +60 -2
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +217 -18
- package/dist/index.js.map +1 -1
- package/dist/ipc/ipc-server.d.ts +20 -0
- package/dist/ipc/ipc-server.d.ts.map +1 -0
- package/dist/ipc/ipc-server.js +152 -0
- package/dist/ipc/ipc-server.js.map +1 -0
- package/dist/jsonrpc/mail-server.d.ts +45 -0
- package/dist/jsonrpc/mail-server.d.ts.map +1 -0
- package/dist/jsonrpc/mail-server.js +284 -0
- package/dist/jsonrpc/mail-server.js.map +1 -0
- package/dist/map/map-client.d.ts +91 -0
- package/dist/map/map-client.d.ts.map +1 -0
- package/dist/map/map-client.js +202 -0
- package/dist/map/map-client.js.map +1 -0
- package/dist/mcp/mcp-server.d.ts +23 -0
- package/dist/mcp/mcp-server.d.ts.map +1 -0
- package/dist/mcp/mcp-server.js +226 -0
- package/dist/mcp/mcp-server.js.map +1 -0
- package/dist/push/notifier.d.ts +49 -0
- package/dist/push/notifier.d.ts.map +1 -0
- package/dist/push/notifier.js +150 -0
- package/dist/push/notifier.js.map +1 -0
- package/dist/registry/warm-registry.d.ts +63 -0
- package/dist/registry/warm-registry.d.ts.map +1 -0
- package/dist/registry/warm-registry.js +173 -0
- package/dist/registry/warm-registry.js.map +1 -0
- package/dist/router/message-router.d.ts +44 -0
- package/dist/router/message-router.d.ts.map +1 -0
- package/dist/router/message-router.js +137 -0
- package/dist/router/message-router.js.map +1 -0
- package/dist/storage/interface.d.ts +31 -0
- package/dist/storage/interface.d.ts.map +1 -0
- package/dist/storage/interface.js +2 -0
- package/dist/storage/interface.js.map +1 -0
- package/dist/storage/memory.d.ts +28 -0
- package/dist/storage/memory.d.ts.map +1 -0
- package/dist/storage/memory.js +118 -0
- package/dist/storage/memory.js.map +1 -0
- package/dist/storage/sqlite.d.ts +35 -0
- package/dist/storage/sqlite.d.ts.map +1 -0
- package/dist/storage/sqlite.js +445 -0
- package/dist/storage/sqlite.js.map +1 -0
- package/dist/traceability/traceability.d.ts +29 -0
- package/dist/traceability/traceability.d.ts.map +1 -0
- package/dist/traceability/traceability.js +150 -0
- package/dist/traceability/traceability.js.map +1 -0
- package/dist/types.d.ts +253 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +3 -0
- package/dist/types.js.map +1 -0
- package/docs/DESIGN.md +1156 -0
- package/docs/PLAN.md +545 -0
- package/hooks/inbox-hook.mjs +119 -0
- package/hooks/register-hook.mjs +69 -0
- package/package.json +33 -25
- package/rules/agent-inbox.md +78 -0
- package/src/federation/address.ts +61 -0
- package/src/federation/connection-manager.ts +458 -0
- package/src/federation/delivery-queue.ts +222 -0
- package/src/federation/index.ts +6 -0
- package/src/federation/routing-engine.ts +188 -0
- package/src/federation/trust.ts +71 -0
- package/src/index.ts +299 -0
- package/src/ipc/ipc-server.ts +180 -0
- package/src/jsonrpc/mail-server.ts +356 -0
- package/src/map/map-client.ts +260 -0
- package/src/mcp/mcp-server.ts +272 -0
- package/src/push/notifier.ts +192 -0
- package/src/registry/warm-registry.ts +210 -0
- package/src/router/message-router.ts +175 -0
- package/src/storage/interface.ts +48 -0
- package/src/storage/memory.ts +145 -0
- package/src/storage/sqlite.ts +645 -0
- package/src/traceability/traceability.ts +183 -0
- package/src/types.ts +287 -0
- package/test/federation/address.test.ts +101 -0
- package/test/federation/connection-manager.test.ts +546 -0
- package/test/federation/delivery-queue.test.ts +159 -0
- package/test/federation/integration.test.ts +823 -0
- package/test/federation/routing-engine.test.ts +117 -0
- package/test/federation/sdk-integration.test.ts +748 -0
- package/test/federation/trust.test.ts +89 -0
- package/test/ipc-jsonrpc.test.ts +113 -0
- package/test/ipc-server.test.ts +138 -0
- package/test/mail-server.test.ts +208 -0
- package/test/map-client.test.ts +408 -0
- package/test/message-router.test.ts +184 -0
- package/test/push-notifier.test.ts +139 -0
- package/test/registry/warm-registry.test.ts +171 -0
- package/test/sqlite-storage.test.ts +243 -0
- package/test/storage.test.ts +196 -0
- package/test/traceability.test.ts +123 -0
- package/tsconfig.json +20 -0
- package/tsup.config.ts +10 -0
- package/vitest.config.ts +8 -0
- package/dist/index.d.mts +0 -2
- package/dist/index.mjs +0 -1
- package/dist/index.mjs.map +0 -1
package/CLAUDE.md
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
# CLAUDE.md — Agent Guide for agent-inbox
|
|
2
|
+
|
|
3
|
+
## What this repo is
|
|
4
|
+
|
|
5
|
+
Agent Inbox is a MAP-native message router for multi-agent systems. It provides structured inbox/outbox semantics, threading, delivery tracking, and cross-system federation on top of MAP (Multi-Agent Protocol) transport.
|
|
6
|
+
|
|
7
|
+
## Build and test
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install # Install dependencies
|
|
11
|
+
npm run build # TypeScript compile (tsc)
|
|
12
|
+
npm test # Run all tests (vitest)
|
|
13
|
+
npm run test:watch # Watch mode
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
All tests use vitest. Run the full suite before committing — there are 210+ tests across 16 files. Tests run fast (<2s total).
|
|
17
|
+
|
|
18
|
+
## Project layout
|
|
19
|
+
|
|
20
|
+
```
|
|
21
|
+
src/
|
|
22
|
+
index.ts Entry point — wires storage, router, federation, IPC, MCP, MAP
|
|
23
|
+
types.ts All type definitions (Message, Agent, Federation types, etc.)
|
|
24
|
+
storage/interface.ts Storage interface (getAgent, putMessage, getInbox, etc.)
|
|
25
|
+
storage/memory.ts InMemoryStorage — Map-based, used in all tests
|
|
26
|
+
storage/sqlite.ts SQLiteStorage — persistent with FTS5 search
|
|
27
|
+
router/message-router.ts Core routing: resolveRecipients, normalizeContent, routeMessage
|
|
28
|
+
federation/
|
|
29
|
+
connection-manager.ts Manages MAP connections + federation peer links + queue flush
|
|
30
|
+
routing-engine.ts Table/broadcast/hierarchical routing strategies
|
|
31
|
+
delivery-queue.ts Offline message queue with TTL, retry, overflow policies
|
|
32
|
+
address.ts Parse "agent@system" addresses (parseAddress, isRemoteAddress)
|
|
33
|
+
trust.ts Allow-list federation trust (canConnect, canRoute stubs)
|
|
34
|
+
registry/warm-registry.ts Agent lifecycle: active → away → expired with TTL timers
|
|
35
|
+
traceability/traceability.ts Auto-create conversations/turns/threads from message events
|
|
36
|
+
ipc/ipc-server.ts UNIX socket server (NDJSON protocol)
|
|
37
|
+
map/map-client.ts MAP SDK wrapper (dynamic import, optional peer dependency)
|
|
38
|
+
mcp/mcp-server.ts MCP tool server (send_message, check_inbox, read_thread, list_agents)
|
|
39
|
+
jsonrpc/mail-server.ts MAP JSON-RPC mail methods
|
|
40
|
+
push/notifier.ts Push notification support
|
|
41
|
+
|
|
42
|
+
test/
|
|
43
|
+
federation/ Federation tests (connection-manager, routing, queue, address, trust, integration, sdk-integration)
|
|
44
|
+
registry/ Warm registry tests
|
|
45
|
+
*.test.ts Core tests (storage, router, traceability, IPC, mail-server, push)
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Key architecture concepts
|
|
49
|
+
|
|
50
|
+
### Message flow
|
|
51
|
+
|
|
52
|
+
1. Message comes in via IPC socket, MCP tool, or MAP connection
|
|
53
|
+
2. `MessageRouter.routeMessage()` resolves recipients, normalizes content, stores the message
|
|
54
|
+
3. Local recipients: message stored in their inbox via storage
|
|
55
|
+
4. Remote recipients (`agent@system`): delegated to `ConnectionManager.route()`
|
|
56
|
+
5. Federation sends via MAP SDK transport (or queues if peer is offline)
|
|
57
|
+
6. Traceability layer auto-creates conversations/threads from events
|
|
58
|
+
|
|
59
|
+
### Federation addressing
|
|
60
|
+
|
|
61
|
+
- `"bob"` — local agent
|
|
62
|
+
- `"bob@system-2"` — agent on a federated system
|
|
63
|
+
- `"@system-2"` — broadcast to system
|
|
64
|
+
|
|
65
|
+
Parsed by `parseAddress()` in `src/federation/address.ts`.
|
|
66
|
+
|
|
67
|
+
### Storage interface
|
|
68
|
+
|
|
69
|
+
All storage operations go through `Storage` interface (`src/storage/interface.ts`). Two implementations: `InMemoryStorage` (tests + ephemeral) and `SQLiteStorage` (persistent). Tests always use `InMemoryStorage`.
|
|
70
|
+
|
|
71
|
+
### MAP SDK is optional
|
|
72
|
+
|
|
73
|
+
The `@multi-agent-protocol/sdk` is a peer dependency loaded dynamically in `src/map/map-client.ts`. Agent Inbox works without it (IPC + MCP only, no MAP transport). The SDK class is injected into `ConnectionManager` when available.
|
|
74
|
+
|
|
75
|
+
### Event-driven internals
|
|
76
|
+
|
|
77
|
+
Components communicate via `EventEmitter`. Key events:
|
|
78
|
+
- `message.created` — emitted by router, consumed by traceability
|
|
79
|
+
- `federation.route` — emitted when no transport, for testing
|
|
80
|
+
- `federation.message.received` — incoming federation message
|
|
81
|
+
- `federation.broadcast` — broadcast strategy fired
|
|
82
|
+
- `routing.updated` — routing table changed
|
|
83
|
+
|
|
84
|
+
## Conventions
|
|
85
|
+
|
|
86
|
+
- **TypeScript strict mode**, ESM output
|
|
87
|
+
- **vitest** for all tests — no jest
|
|
88
|
+
- **No mocking frameworks** — tests use hand-rolled mocks and the `InMemoryStorage` directly
|
|
89
|
+
- IDs generated with `ulid` or `crypto.randomUUID()`
|
|
90
|
+
- Content normalization: string payloads auto-wrapped to `{ type: "text", text: "..." }`
|
|
91
|
+
- Federation tests use `MockMapServer` (in-process broker) in `test/federation/sdk-integration.test.ts`
|
|
92
|
+
|
|
93
|
+
## Known design gaps
|
|
94
|
+
|
|
95
|
+
- **Broadcast/hierarchical routing strategies** are unreachable through `ConnectionManager.route()` for system-qualified addresses (`bob@system-2`). `resolveRoute()` always returns `addr.system` directly, never falling through to `handleUnknownRoute()`. Agent-only addresses that would trigger it are filtered by `isRemoteAddress()`. Documented in `test/federation/integration.test.ts` and `test/federation/sdk-integration.test.ts`.
|
|
96
|
+
|
|
97
|
+
## Common tasks
|
|
98
|
+
|
|
99
|
+
### Adding a new MCP tool
|
|
100
|
+
1. Add tool definition in `src/mcp/mcp-server.ts`
|
|
101
|
+
2. Implement handler that delegates to router/storage
|
|
102
|
+
3. Add test in `test/` (currently no dedicated mcp test file — tools tested through integration)
|
|
103
|
+
|
|
104
|
+
### Adding a new federation feature
|
|
105
|
+
1. Types go in `src/types.ts`
|
|
106
|
+
2. Logic in `src/federation/` (connection-manager, routing-engine, etc.)
|
|
107
|
+
3. Unit test in `test/federation/`
|
|
108
|
+
4. Integration test in `test/federation/integration.test.ts` or `test/federation/sdk-integration.test.ts`
|
|
109
|
+
|
|
110
|
+
### Adding a new storage method
|
|
111
|
+
1. Add to interface in `src/storage/interface.ts`
|
|
112
|
+
2. Implement in both `memory.ts` and `sqlite.ts`
|
|
113
|
+
3. Test in `test/storage.test.ts` and `test/sqlite-storage.test.ts`
|
package/README.md
CHANGED
|
@@ -1 +1,195 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Agent Inbox
|
|
2
|
+
|
|
3
|
+
A MAP-native message router for multi-agent systems. Provides structured inbox/outbox semantics, message threading, delivery tracking, and cross-system federation on top of the [Multi-Agent Protocol](https://github.com/anthropics/multi-agent-protocol) transport layer.
|
|
4
|
+
|
|
5
|
+
Built to work with [claude-code-swarm](https://github.com/alexngai/claude-code-swarm) and any MAP-compatible agent framework.
|
|
6
|
+
|
|
7
|
+
## What it does
|
|
8
|
+
|
|
9
|
+
- **Message routing** — Send messages between agents with `to`, `cc`, `bcc` semantics. Messages are stored in per-agent inboxes with delivery and read tracking.
|
|
10
|
+
- **Threading** — Messages can be grouped by `thread_tag` and linked via `in_reply_to` for conversation threading.
|
|
11
|
+
- **Traceability** — Auto-creates conversations, turns, and threads from messaging events following MAP's mail conventions.
|
|
12
|
+
- **Federation** — Route messages across independent systems via MAP federation. Supports configurable routing strategies (table, broadcast, hierarchical).
|
|
13
|
+
- **Multiple interfaces** — IPC (UNIX socket), MCP tools (stdio), MAP JSON-RPC, and push notifications.
|
|
14
|
+
|
|
15
|
+
## Architecture
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
Claude Code Session
|
|
19
|
+
|
|
|
20
|
+
Plugin (map-hook.mjs)
|
|
21
|
+
|
|
|
22
|
+
+-- spawn/done/trajectory --> MAP Sidecar (lifecycle)
|
|
23
|
+
|
|
|
24
|
+
+-- send/emit (messaging) --> Agent Inbox (routing + storage)
|
|
25
|
+
|
|
|
26
|
+
+-- Local delivery (same system)
|
|
27
|
+
+-- Federation (cross-system via MAP)
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Agent Inbox runs as an independent service alongside the MAP sidecar. The plugin dispatches messaging commands to Agent Inbox and lifecycle commands to the sidecar.
|
|
31
|
+
|
|
32
|
+
## Quick start
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
npm install
|
|
36
|
+
npm run build
|
|
37
|
+
npm start
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### Environment variables
|
|
41
|
+
|
|
42
|
+
| Variable | Default | Description |
|
|
43
|
+
|----------|---------|-------------|
|
|
44
|
+
| `INBOX_SOCKET_PATH` | `~/.claude/agent-inbox/inbox.sock` | IPC socket path |
|
|
45
|
+
| `INBOX_SCOPE` | `default` | Default message scope |
|
|
46
|
+
| `INBOX_SYSTEM_ID` | auto-generated | System identity for federation |
|
|
47
|
+
| `MAP_SERVER_URL` | — | MAP server URL (optional) |
|
|
48
|
+
| `MAP_AGENT_NAME` | `agent-inbox` | Agent name for MAP connection |
|
|
49
|
+
|
|
50
|
+
### As an MCP tool server
|
|
51
|
+
|
|
52
|
+
Agent Inbox exposes 4 MCP tools that agents can call directly:
|
|
53
|
+
|
|
54
|
+
| Tool | Description |
|
|
55
|
+
|------|-------------|
|
|
56
|
+
| `send_message` | Send a message to one or more agents (supports replies via `inReplyTo`) |
|
|
57
|
+
| `check_inbox` | Check an agent's inbox for unread messages (auto-registers the agent) |
|
|
58
|
+
| `read_thread` | Read all messages in a thread by `threadTag` |
|
|
59
|
+
| `list_agents` | List registered agents (local and optionally federated) |
|
|
60
|
+
|
|
61
|
+
### IPC protocol
|
|
62
|
+
|
|
63
|
+
NDJSON over UNIX socket. Commands:
|
|
64
|
+
|
|
65
|
+
```json
|
|
66
|
+
{"action": "send", "from": "alice", "to": "bob", "payload": "Hello"}
|
|
67
|
+
{"action": "notify", "event": {"type": "agent.spawn", "agent": {...}}}
|
|
68
|
+
{"action": "ping"}
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### MAP JSON-RPC
|
|
72
|
+
|
|
73
|
+
When connected to a MAP server, Agent Inbox registers JSON-RPC methods:
|
|
74
|
+
|
|
75
|
+
- `mail/send` — Send a message
|
|
76
|
+
- `mail/inbox` — Check inbox
|
|
77
|
+
- `mail/thread` — Read a thread
|
|
78
|
+
- `mail/ack` — Acknowledge a message
|
|
79
|
+
- `mail/search` — Full-text search (requires SQLite storage)
|
|
80
|
+
|
|
81
|
+
## Federation
|
|
82
|
+
|
|
83
|
+
Agents on different systems can message each other using `agent@system` addressing:
|
|
84
|
+
|
|
85
|
+
```
|
|
86
|
+
alice@system-1 --> bob@system-2
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### Configuration
|
|
90
|
+
|
|
91
|
+
Federation peers are configured explicitly:
|
|
92
|
+
|
|
93
|
+
```typescript
|
|
94
|
+
await inbox.federate({
|
|
95
|
+
systemId: "partner-system",
|
|
96
|
+
url: "ws://partner.example.com:3001",
|
|
97
|
+
exposure: { agents: "all" },
|
|
98
|
+
});
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### Routing strategies
|
|
102
|
+
|
|
103
|
+
| Strategy | Behavior |
|
|
104
|
+
|----------|----------|
|
|
105
|
+
| `table` (default) | In-memory routing table populated from peer exposure data. TTL-based expiry. |
|
|
106
|
+
| `broadcast` | Forward to all connected peers. First responder wins. |
|
|
107
|
+
| `hierarchical` | Local table first, then delegate to upstream hubs. |
|
|
108
|
+
|
|
109
|
+
### Trust
|
|
110
|
+
|
|
111
|
+
Federation connections are gated by an allow-list. Only systems in `allowedServers` can establish federation links.
|
|
112
|
+
|
|
113
|
+
### Delivery queue
|
|
114
|
+
|
|
115
|
+
Messages to offline peers are queued with configurable TTL, overflow policy, and retry strategy. Queues flush automatically on reconnect.
|
|
116
|
+
|
|
117
|
+
## Storage
|
|
118
|
+
|
|
119
|
+
Two storage backends:
|
|
120
|
+
|
|
121
|
+
- **InMemoryStorage** — Default. Fast, no dependencies. Data lost on restart.
|
|
122
|
+
- **SQLiteStorage** — Persistent storage with FTS5 full-text search. Requires `better-sqlite3`.
|
|
123
|
+
|
|
124
|
+
## Project structure
|
|
125
|
+
|
|
126
|
+
```
|
|
127
|
+
src/
|
|
128
|
+
index.ts Entry point — wires all components
|
|
129
|
+
types.ts Core type definitions
|
|
130
|
+
storage/
|
|
131
|
+
interface.ts Storage interface
|
|
132
|
+
memory.ts In-memory implementation
|
|
133
|
+
sqlite.ts SQLite implementation (persistent + FTS5)
|
|
134
|
+
router/
|
|
135
|
+
message-router.ts Message routing: resolve recipients, local vs remote
|
|
136
|
+
federation/
|
|
137
|
+
connection-manager.ts MAP connections + federation peer management
|
|
138
|
+
routing-engine.ts Configurable routing (table/broadcast/hierarchical)
|
|
139
|
+
delivery-queue.ts Offline message queue with TTL + retry
|
|
140
|
+
address.ts Parse/resolve agent@system addresses
|
|
141
|
+
trust.ts Federation trust policies (allow-list)
|
|
142
|
+
registry/
|
|
143
|
+
warm-registry.ts Agent lifecycle: active/away/expired + TTL
|
|
144
|
+
traceability/
|
|
145
|
+
traceability.ts Auto-create conversations/turns/threads
|
|
146
|
+
ipc/
|
|
147
|
+
ipc-server.ts UNIX socket server (NDJSON protocol)
|
|
148
|
+
map/
|
|
149
|
+
map-client.ts MAP SDK connection wrapper
|
|
150
|
+
mcp/
|
|
151
|
+
mcp-server.ts MCP tool server (stdio transport)
|
|
152
|
+
jsonrpc/
|
|
153
|
+
mail-server.ts MAP JSON-RPC mail methods
|
|
154
|
+
push/
|
|
155
|
+
notifier.ts Push notification support
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
## Testing
|
|
159
|
+
|
|
160
|
+
```bash
|
|
161
|
+
npm test # Run all tests
|
|
162
|
+
npm run test:watch # Watch mode
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
210 tests across 16 test files covering:
|
|
166
|
+
|
|
167
|
+
- Storage (in-memory + SQLite)
|
|
168
|
+
- Message routing and content normalization
|
|
169
|
+
- Traceability (auto-conversation/thread creation)
|
|
170
|
+
- IPC server (NDJSON protocol, concurrent clients)
|
|
171
|
+
- Federation (connection manager, routing engine, delivery queue, address parsing, trust)
|
|
172
|
+
- Federation integration (end-to-end multi-system scenarios)
|
|
173
|
+
- SDK integration (two-system tests with in-process mock MAP server)
|
|
174
|
+
- MAP JSON-RPC mail methods
|
|
175
|
+
- Warm agent registry lifecycle
|
|
176
|
+
- Push notifications
|
|
177
|
+
|
|
178
|
+
## Development
|
|
179
|
+
|
|
180
|
+
```bash
|
|
181
|
+
npm run dev # TypeScript watch mode
|
|
182
|
+
npm test # Run tests
|
|
183
|
+
npm run build # Production build
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
Requires Node.js >= 18.
|
|
187
|
+
|
|
188
|
+
## Further reading
|
|
189
|
+
|
|
190
|
+
- [docs/DESIGN.md](docs/DESIGN.md) — Detailed architecture and design decisions
|
|
191
|
+
- [docs/PLAN.md](docs/PLAN.md) — Implementation plan and phasing
|
|
192
|
+
|
|
193
|
+
## License
|
|
194
|
+
|
|
195
|
+
MIT
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { FederatedAddress } from "../types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Parse a federated address string into its components.
|
|
4
|
+
*
|
|
5
|
+
* Formats:
|
|
6
|
+
* "agent-id" → { agent: "agent-id" } (local)
|
|
7
|
+
* "agent-id@system-id" → { agent: "agent-id", system: "system-id" } (federated)
|
|
8
|
+
* "@system-id" → { system: "system-id" } (broadcast to system)
|
|
9
|
+
* "agent-id@system/scope" → { agent: "agent-id", system: "system", scope: "scope" }
|
|
10
|
+
*/
|
|
11
|
+
export declare function parseAddress(address: string): FederatedAddress;
|
|
12
|
+
/**
|
|
13
|
+
* Serialize a FederatedAddress back to string form.
|
|
14
|
+
*/
|
|
15
|
+
export declare function formatAddress(addr: FederatedAddress): string;
|
|
16
|
+
/**
|
|
17
|
+
* Check if an address targets a remote system.
|
|
18
|
+
*/
|
|
19
|
+
export declare function isRemoteAddress(addr: FederatedAddress): boolean;
|
|
20
|
+
/**
|
|
21
|
+
* Check if an address is a broadcast (no specific agent).
|
|
22
|
+
*/
|
|
23
|
+
export declare function isBroadcastAddress(addr: FederatedAddress): boolean;
|
|
24
|
+
//# sourceMappingURL=address.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"address.d.ts","sourceRoot":"","sources":["../../src/federation/address.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAEpD;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,gBAAgB,CAqB9D;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,gBAAgB,GAAG,MAAM,CAS5D;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,gBAAgB,GAAG,OAAO,CAE/D;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,gBAAgB,GAAG,OAAO,CAElE"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parse a federated address string into its components.
|
|
3
|
+
*
|
|
4
|
+
* Formats:
|
|
5
|
+
* "agent-id" → { agent: "agent-id" } (local)
|
|
6
|
+
* "agent-id@system-id" → { agent: "agent-id", system: "system-id" } (federated)
|
|
7
|
+
* "@system-id" → { system: "system-id" } (broadcast to system)
|
|
8
|
+
* "agent-id@system/scope" → { agent: "agent-id", system: "system", scope: "scope" }
|
|
9
|
+
*/
|
|
10
|
+
export function parseAddress(address) {
|
|
11
|
+
const atIdx = address.indexOf("@");
|
|
12
|
+
if (atIdx === -1) {
|
|
13
|
+
// No @ — local address
|
|
14
|
+
return { agent: address };
|
|
15
|
+
}
|
|
16
|
+
const agent = atIdx > 0 ? address.slice(0, atIdx) : undefined;
|
|
17
|
+
const rest = address.slice(atIdx + 1);
|
|
18
|
+
// Check for scope separator (/)
|
|
19
|
+
const slashIdx = rest.indexOf("/");
|
|
20
|
+
if (slashIdx === -1) {
|
|
21
|
+
return { agent, system: rest };
|
|
22
|
+
}
|
|
23
|
+
return {
|
|
24
|
+
agent,
|
|
25
|
+
system: rest.slice(0, slashIdx),
|
|
26
|
+
scope: rest.slice(slashIdx + 1),
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Serialize a FederatedAddress back to string form.
|
|
31
|
+
*/
|
|
32
|
+
export function formatAddress(addr) {
|
|
33
|
+
let result = addr.agent ?? "";
|
|
34
|
+
if (addr.system) {
|
|
35
|
+
result += `@${addr.system}`;
|
|
36
|
+
if (addr.scope) {
|
|
37
|
+
result += `/${addr.scope}`;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return result;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Check if an address targets a remote system.
|
|
44
|
+
*/
|
|
45
|
+
export function isRemoteAddress(addr) {
|
|
46
|
+
return addr.system !== undefined;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Check if an address is a broadcast (no specific agent).
|
|
50
|
+
*/
|
|
51
|
+
export function isBroadcastAddress(addr) {
|
|
52
|
+
return addr.system !== undefined && addr.agent === undefined;
|
|
53
|
+
}
|
|
54
|
+
//# sourceMappingURL=address.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"address.js","sourceRoot":"","sources":["../../src/federation/address.ts"],"names":[],"mappings":"AAEA;;;;;;;;GAQG;AACH,MAAM,UAAU,YAAY,CAAC,OAAe;IAC1C,MAAM,KAAK,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IACnC,IAAI,KAAK,KAAK,CAAC,CAAC,EAAE,CAAC;QACjB,uBAAuB;QACvB,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC;IAC5B,CAAC;IAED,MAAM,KAAK,GAAG,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IAC9D,MAAM,IAAI,GAAG,OAAO,CAAC,KAAK,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC;IAEtC,gCAAgC;IAChC,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IACnC,IAAI,QAAQ,KAAK,CAAC,CAAC,EAAE,CAAC;QACpB,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;IACjC,CAAC;IAED,OAAO;QACL,KAAK;QACL,MAAM,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,QAAQ,CAAC;QAC/B,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,QAAQ,GAAG,CAAC,CAAC;KAChC,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,aAAa,CAAC,IAAsB;IAClD,IAAI,MAAM,GAAG,IAAI,CAAC,KAAK,IAAI,EAAE,CAAC;IAC9B,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;QAChB,MAAM,IAAI,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;QAC5B,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;YACf,MAAM,IAAI,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;QAC7B,CAAC;IACH,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,eAAe,CAAC,IAAsB;IACpD,OAAO,IAAI,CAAC,MAAM,KAAK,SAAS,CAAC;AACnC,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,kBAAkB,CAAC,IAAsB;IACvD,OAAO,IAAI,CAAC,MAAM,KAAK,SAAS,IAAI,IAAI,CAAC,KAAK,KAAK,SAAS,CAAC;AAC/D,CAAC"}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { EventEmitter } from "node:events";
|
|
2
|
+
import type { FederationConfig, FederationLink, FederationPeerConfig, Message, SystemId } from "../types.js";
|
|
3
|
+
import type { MapAgentConnectionClass } from "../map/map-client.js";
|
|
4
|
+
import { RoutingEngine } from "./routing-engine.js";
|
|
5
|
+
import { DeliveryQueue } from "./delivery-queue.js";
|
|
6
|
+
import { TrustManager } from "./trust.js";
|
|
7
|
+
export interface DeliveryResult {
|
|
8
|
+
delivered: boolean;
|
|
9
|
+
peerId?: string;
|
|
10
|
+
queued?: boolean;
|
|
11
|
+
error?: string;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Callback for handling incoming federation messages.
|
|
15
|
+
* Wired to router.routeMessage() by index.ts.
|
|
16
|
+
*/
|
|
17
|
+
export type IncomingMessageHandler = (incoming: {
|
|
18
|
+
from: string;
|
|
19
|
+
peerId: string;
|
|
20
|
+
payload: unknown;
|
|
21
|
+
meta?: Record<string, unknown>;
|
|
22
|
+
}) => void;
|
|
23
|
+
/**
|
|
24
|
+
* Manages MAP connections and federation peer links.
|
|
25
|
+
*
|
|
26
|
+
* Coordinates routing, delivery queuing, and trust enforcement
|
|
27
|
+
* for cross-system messaging. When an SDK connect function is provided,
|
|
28
|
+
* opens real MAP connections to federation peers for actual message transport.
|
|
29
|
+
*/
|
|
30
|
+
export declare class ConnectionManager {
|
|
31
|
+
private events;
|
|
32
|
+
private config;
|
|
33
|
+
private peers;
|
|
34
|
+
private connections;
|
|
35
|
+
private systemId;
|
|
36
|
+
private sdkClass;
|
|
37
|
+
private onIncoming;
|
|
38
|
+
readonly routing: RoutingEngine;
|
|
39
|
+
readonly queue: DeliveryQueue;
|
|
40
|
+
readonly trust: TrustManager;
|
|
41
|
+
constructor(events: EventEmitter, config?: FederationConfig, opts?: {
|
|
42
|
+
sdkClass?: MapAgentConnectionClass;
|
|
43
|
+
onIncomingMessage?: IncomingMessageHandler;
|
|
44
|
+
});
|
|
45
|
+
/**
|
|
46
|
+
* Get the resolved system ID for this Agent Inbox instance.
|
|
47
|
+
*/
|
|
48
|
+
getSystemId(): SystemId;
|
|
49
|
+
/**
|
|
50
|
+
* Establish federation with a peer. Uses MAP federation/connect protocol.
|
|
51
|
+
* If an SDK class was injected, opens a real MAP connection to the peer.
|
|
52
|
+
* Returns the federation link, or throws if trust check fails.
|
|
53
|
+
*/
|
|
54
|
+
federate(peer: FederationPeerConfig): Promise<FederationLink>;
|
|
55
|
+
/**
|
|
56
|
+
* Disconnect from a federation peer.
|
|
57
|
+
* Closes the real MAP connection if one exists.
|
|
58
|
+
*/
|
|
59
|
+
disconnect(peerId: string): Promise<void>;
|
|
60
|
+
/**
|
|
61
|
+
* Route a message to the correct federation peer.
|
|
62
|
+
* Resolves the address, checks trust, and delivers or queues.
|
|
63
|
+
* If a real connection exists, sends via conn.send(); otherwise emits event.
|
|
64
|
+
*/
|
|
65
|
+
route(message: Message): Promise<DeliveryResult>;
|
|
66
|
+
/**
|
|
67
|
+
* Send a message to a peer via real connection or event emission.
|
|
68
|
+
* Returns true if send succeeded (or event was emitted).
|
|
69
|
+
*/
|
|
70
|
+
private sendToPeer;
|
|
71
|
+
/**
|
|
72
|
+
* Handle an incoming message from a federation peer connection.
|
|
73
|
+
* Delegates to the injected message handler (wired to router.routeMessage).
|
|
74
|
+
*/
|
|
75
|
+
private handlePeerMessage;
|
|
76
|
+
/**
|
|
77
|
+
* Handle routing to an unknown agent. Behavior depends on strategy.
|
|
78
|
+
*/
|
|
79
|
+
private handleUnknownRoute;
|
|
80
|
+
/**
|
|
81
|
+
* Get all federation peer links.
|
|
82
|
+
*/
|
|
83
|
+
getPeers(): FederationLink[];
|
|
84
|
+
/**
|
|
85
|
+
* Get only connected peers.
|
|
86
|
+
*/
|
|
87
|
+
getConnectedPeers(): FederationLink[];
|
|
88
|
+
/**
|
|
89
|
+
* Get a specific peer link.
|
|
90
|
+
*/
|
|
91
|
+
getPeer(peerId: string): FederationLink | undefined;
|
|
92
|
+
/**
|
|
93
|
+
* Check if a peer is connected.
|
|
94
|
+
*/
|
|
95
|
+
isConnected(peerId: string): boolean;
|
|
96
|
+
/**
|
|
97
|
+
* Check if a real MAP SDK connection exists for a peer.
|
|
98
|
+
*/
|
|
99
|
+
hasTransport(peerId: string): boolean;
|
|
100
|
+
/**
|
|
101
|
+
* Clean up all state. Call on shutdown.
|
|
102
|
+
*/
|
|
103
|
+
destroy(): Promise<void>;
|
|
104
|
+
/**
|
|
105
|
+
* Resolve the system ID using tiered precedence:
|
|
106
|
+
* 1. Explicit config (INBOX_SYSTEM_ID)
|
|
107
|
+
* 2. Auto-generated (persisted to file for stability across restarts)
|
|
108
|
+
*
|
|
109
|
+
* Note: Tier 2 (MAP systemInfo) is handled after MAP connection is established.
|
|
110
|
+
*/
|
|
111
|
+
private resolveSystemId;
|
|
112
|
+
/**
|
|
113
|
+
* Update system ID from MAP server's systemInfo (Tier 2).
|
|
114
|
+
* Only used if no explicit config was set.
|
|
115
|
+
*/
|
|
116
|
+
updateSystemIdFromMap(mapSystemName: string): void;
|
|
117
|
+
}
|
|
118
|
+
//# sourceMappingURL=connection-manager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"connection-manager.d.ts","sourceRoot":"","sources":["../../src/federation/connection-manager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAK3C,OAAO,KAAK,EAEV,gBAAgB,EAChB,cAAc,EACd,oBAAoB,EACpB,OAAO,EACP,QAAQ,EACT,MAAM,aAAa,CAAC;AACrB,OAAO,KAAK,EAEV,uBAAuB,EAExB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAG1C,MAAM,WAAW,cAAc;IAC7B,SAAS,EAAE,OAAO,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;GAGG;AACH,MAAM,MAAM,sBAAsB,GAAG,CAAC,QAAQ,EAAE;IAC9C,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAChC,KAAK,IAAI,CAAC;AAEX;;;;;;GAMG;AACH,qBAAa,iBAAiB;IAW1B,OAAO,CAAC,MAAM;IACd,OAAO,CAAC,MAAM;IAXhB,OAAO,CAAC,KAAK,CAAqC;IAClD,OAAO,CAAC,WAAW,CAAoC;IACvD,OAAO,CAAC,QAAQ,CAAW;IAC3B,OAAO,CAAC,QAAQ,CAAiC;IACjD,OAAO,CAAC,UAAU,CAAgC;IAClD,QAAQ,CAAC,OAAO,EAAE,aAAa,CAAC;IAChC,QAAQ,CAAC,KAAK,EAAE,aAAa,CAAC;IAC9B,QAAQ,CAAC,KAAK,EAAE,YAAY,CAAC;gBAGnB,MAAM,EAAE,YAAY,EACpB,MAAM,GAAE,gBAAqB,EACrC,IAAI,CAAC,EAAE;QACL,QAAQ,CAAC,EAAE,uBAAuB,CAAC;QACnC,iBAAiB,CAAC,EAAE,sBAAsB,CAAC;KAC5C;IA6BH;;OAEG;IACH,WAAW,IAAI,QAAQ;IAIvB;;;;OAIG;IACG,QAAQ,CAAC,IAAI,EAAE,oBAAoB,GAAG,OAAO,CAAC,cAAc,CAAC;IAyDnE;;;OAGG;IACG,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAqB/C;;;;OAIG;IACG,KAAK,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,cAAc,CAAC;IA6CtD;;;OAGG;YACW,UAAU;IAkCxB;;;OAGG;IACH,OAAO,CAAC,iBAAiB;IAazB;;OAEG;YACW,kBAAkB;IA0DhC;;OAEG;IACH,QAAQ,IAAI,cAAc,EAAE;IAI5B;;OAEG;IACH,iBAAiB,IAAI,cAAc,EAAE;IAMrC;;OAEG;IACH,OAAO,CAAC,MAAM,EAAE,MAAM,GAAG,cAAc,GAAG,SAAS;IAInD;;OAEG;IACH,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO;IAIpC;;OAEG;IACH,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO;IAIrC;;OAEG;IACG,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAgB9B;;;;;;OAMG;IACH,OAAO,CAAC,eAAe;IAgCvB;;;OAGG;IACH,qBAAqB,CAAC,aAAa,EAAE,MAAM,GAAG,IAAI;CAKnD"}
|