@dmsdc-ai/aigentry-telepty 0.1.88 → 0.1.89
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/cli.js +137 -20
- package/daemon.js +238 -41
- package/package.json +1 -1
- package/protocol/mailbox.md +244 -0
- package/session-state.js +496 -0
- package/src/mailbox/config.js +36 -0
- package/src/mailbox/delivery.js +132 -0
- package/src/mailbox/index.js +384 -0
- package/src/mailbox/notifier.js +103 -0
- package/src/mailbox/storage.js +185 -0
package/package.json
CHANGED
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
# Aigentry Mailbox Protocol — SSOT
|
|
2
|
+
|
|
3
|
+
**Version**: 1.0-draft
|
|
4
|
+
**Status**: Design
|
|
5
|
+
**Scope**: Transport-agnostic protocol spec (message format, ACK semantics, state machine)
|
|
6
|
+
**Date**: 2026-04-07
|
|
7
|
+
|
|
8
|
+
This document is the single source of truth for the aigentry mailbox protocol.
|
|
9
|
+
Implementation details (storage backend, locking, transport) are in `aigentry-mailbox` crate spec.
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## 1. Purpose
|
|
14
|
+
|
|
15
|
+
The mailbox protocol defines guaranteed, ordered, ACK-able message delivery between aigentry sessions (aterm workspaces, telepty sessions, orchestrators). It is transport-agnostic: the same protocol runs over files, Unix sockets, HTTP, or WebSocket.
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## 2. Message Format
|
|
20
|
+
|
|
21
|
+
All messages are JSON objects. Field names use snake_case (Rust) / camelCase (TypeScript/Node.js) — both representations are valid; implementations must accept both via alias.
|
|
22
|
+
|
|
23
|
+
```json
|
|
24
|
+
{
|
|
25
|
+
"msg_id": "orchestrator:1743999600123456789",
|
|
26
|
+
"from": "aigentry-orchestrator-claude",
|
|
27
|
+
"to": "aigentry-analyst-claude",
|
|
28
|
+
"payload": "analyze the auth module",
|
|
29
|
+
"created_at": 1743999600,
|
|
30
|
+
"attempt": 0
|
|
31
|
+
}
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
| Field | Type | Required | Description |
|
|
35
|
+
|-------|------|----------|-------------|
|
|
36
|
+
| `msg_id` | string | yes | Globally unique message ID. Format: `{from}:{nanoseconds}` or UUID. Used for idempotency and ACK. |
|
|
37
|
+
| `from` | string | yes | Sender session ID or alias. |
|
|
38
|
+
| `to` | string | yes | Target session ID or alias. |
|
|
39
|
+
| `payload` | string | yes | Message body. Arbitrary UTF-8. Typically a CLI command string. |
|
|
40
|
+
| `created_at` | uint64 | yes | Unix timestamp (seconds) when message was first created by sender. Immutable across retries. |
|
|
41
|
+
| `attempt` | uint32 | yes | Delivery attempt count. 0 = first attempt. Incremented by mailbox on NACK+retry. |
|
|
42
|
+
|
|
43
|
+
### msg_id Requirements
|
|
44
|
+
- MUST be unique per logical message (not per delivery attempt)
|
|
45
|
+
- MUST remain the same across retries
|
|
46
|
+
- MUST be stable: same logical send operation always produces the same msg_id
|
|
47
|
+
- Recommended format: `"{from}:{created_at_nanos}"` — deterministic, no UUID library needed
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
## 3. Message State Machine
|
|
52
|
+
|
|
53
|
+
```
|
|
54
|
+
┌─────────────────────────────────────────────┐
|
|
55
|
+
│ enqueue() │
|
|
56
|
+
▼ │
|
|
57
|
+
PENDING ──────── dequeue() ──────► IN_FLIGHT │ (idempotent: duplicate
|
|
58
|
+
▲ │ │ enqueue → PENDING skip)
|
|
59
|
+
│ ack() ───┤
|
|
60
|
+
│ ▼
|
|
61
|
+
│ ACKED (terminal)
|
|
62
|
+
│
|
|
63
|
+
│ nack(reason) ────► NACKED
|
|
64
|
+
│ │
|
|
65
|
+
│ attempt < max ────────┘──── re-enqueue ──► PENDING
|
|
66
|
+
│ attempt ≥ max ──────────────────────────► DEAD_LETTER (terminal)
|
|
67
|
+
│
|
|
68
|
+
└──── TTL exceeded ───────────────────────────────────► EXPIRED (terminal)
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### States
|
|
72
|
+
|
|
73
|
+
| State | Terminal | Description |
|
|
74
|
+
|-------|----------|-------------|
|
|
75
|
+
| `pending` | no | Awaiting dequeue by receiver |
|
|
76
|
+
| `in_flight` | no | Dequeued, receiver processing, awaiting ACK |
|
|
77
|
+
| `acked` | yes | Delivery confirmed by receiver |
|
|
78
|
+
| `nacked` | no | Delivery failed, scheduled for retry |
|
|
79
|
+
| `dead_letter` | yes | Exhausted retry count |
|
|
80
|
+
| `expired` | yes | TTL exceeded before delivery |
|
|
81
|
+
|
|
82
|
+
### State Transition Rules
|
|
83
|
+
1. Only `pending` → `in_flight` (via `dequeue`)
|
|
84
|
+
2. Only `in_flight` → `acked` or `nacked` (via `ack`/`nack`)
|
|
85
|
+
3. `nacked` with `attempt < max_retries` → new `pending` entry (same msg_id, attempt+1)
|
|
86
|
+
4. `nacked` with `attempt >= max_retries` → `dead_letter`
|
|
87
|
+
5. `pending` or `in_flight` past TTL → `expired` (by DeliveryEngine sweep)
|
|
88
|
+
6. Terminal states are immutable
|
|
89
|
+
|
|
90
|
+
---
|
|
91
|
+
|
|
92
|
+
## 4. ACK Semantics
|
|
93
|
+
|
|
94
|
+
### enqueue → EnqueueAck
|
|
95
|
+
```
|
|
96
|
+
enqueue(msg) → { msg_id, queued: bool, pending: usize }
|
|
97
|
+
```
|
|
98
|
+
- `queued: true` — message newly added
|
|
99
|
+
- `queued: false` — msg_id already seen (idempotent, no-op). Safe to call multiple times.
|
|
100
|
+
|
|
101
|
+
### dequeue
|
|
102
|
+
```
|
|
103
|
+
dequeue(session_id) → Option<Message>
|
|
104
|
+
```
|
|
105
|
+
- Returns oldest `pending` message
|
|
106
|
+
- Transitions it to `in_flight`
|
|
107
|
+
- Returns `None` if no pending messages
|
|
108
|
+
|
|
109
|
+
### ack
|
|
110
|
+
```
|
|
111
|
+
ack(session_id, msg_id) → Result<()>
|
|
112
|
+
```
|
|
113
|
+
- Transitions `in_flight` → `acked`
|
|
114
|
+
- MUST be called after successful delivery to PTY or host
|
|
115
|
+
|
|
116
|
+
### nack
|
|
117
|
+
```
|
|
118
|
+
nack(session_id, msg_id, reason: string) → Result<()>
|
|
119
|
+
```
|
|
120
|
+
- Transitions `in_flight` → `nacked`
|
|
121
|
+
- Mailbox schedules retry with backoff: `retry_delay = base_backoff_secs × 2^attempt`
|
|
122
|
+
- After `max_retries` NACKs: transitions to `dead_letter`
|
|
123
|
+
|
|
124
|
+
---
|
|
125
|
+
|
|
126
|
+
## 5. Retry Policy
|
|
127
|
+
|
|
128
|
+
| Parameter | Default | Description |
|
|
129
|
+
|-----------|---------|-------------|
|
|
130
|
+
| `max_retries` | 3 | NACK count before dead-lettering |
|
|
131
|
+
| `base_backoff_secs` | 5 | First retry delay (seconds) |
|
|
132
|
+
| Backoff strategy | Exponential | `delay = base × 2^attempt`: 5s, 10s, 20s |
|
|
133
|
+
| `inflight_timeout_secs` | 30 | Auto-nack if ACK not received within this window |
|
|
134
|
+
|
|
135
|
+
---
|
|
136
|
+
|
|
137
|
+
## 6. Dead Letter Queue
|
|
138
|
+
|
|
139
|
+
Messages in `dead_letter` state are preserved indefinitely (no TTL). Dead letter entries contain:
|
|
140
|
+
|
|
141
|
+
```json
|
|
142
|
+
{
|
|
143
|
+
"msg_id": "orchestrator:...",
|
|
144
|
+
"from": "...",
|
|
145
|
+
"to": "...",
|
|
146
|
+
"payload": "...",
|
|
147
|
+
"reason": "max_retries exhausted",
|
|
148
|
+
"failed_at": 1744086000,
|
|
149
|
+
"attempts": 3
|
|
150
|
+
}
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
Dead letter queue is inspectable (`peek_dead_letter(session_id)`) and purgeable (`purge_dead_letter(session_id)`).
|
|
154
|
+
|
|
155
|
+
---
|
|
156
|
+
|
|
157
|
+
## 7. Ordering Guarantee
|
|
158
|
+
|
|
159
|
+
- Messages are delivered FIFO per `(from, to)` pair
|
|
160
|
+
- Messages from different senders to the same receiver are interleaved by `created_at`
|
|
161
|
+
- No total ordering across all receivers
|
|
162
|
+
|
|
163
|
+
---
|
|
164
|
+
|
|
165
|
+
## 8. Idempotency
|
|
166
|
+
|
|
167
|
+
- `enqueue` with duplicate `msg_id` is a no-op (returns `queued: false`)
|
|
168
|
+
- `ack` with already-acked `msg_id` is a no-op (returns `Ok(())`)
|
|
169
|
+
- `nack` with already-dead-lettered `msg_id` is a no-op
|
|
170
|
+
- Implementations MUST enforce idempotency at the storage layer, not the caller
|
|
171
|
+
|
|
172
|
+
---
|
|
173
|
+
|
|
174
|
+
## 9. Protocol Versioning
|
|
175
|
+
|
|
176
|
+
Messages MAY include `"protocol_version": "1.0"` field. Receivers MUST ignore unknown fields (forward compatibility). Senders SHOULD include version for diagnostics.
|
|
177
|
+
|
|
178
|
+
---
|
|
179
|
+
|
|
180
|
+
## 10. TypeScript Interface (Node.js / telepty)
|
|
181
|
+
|
|
182
|
+
```typescript
|
|
183
|
+
// message.ts
|
|
184
|
+
|
|
185
|
+
export interface Message {
|
|
186
|
+
msg_id: string; // or msgId (both accepted)
|
|
187
|
+
from: string;
|
|
188
|
+
to: string;
|
|
189
|
+
payload: string;
|
|
190
|
+
created_at: number; // or createdAt (Unix seconds)
|
|
191
|
+
attempt: number;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
export interface MessageSummary {
|
|
195
|
+
msg_id: string;
|
|
196
|
+
from: string;
|
|
197
|
+
created_at: number;
|
|
198
|
+
attempt: number;
|
|
199
|
+
state: MessageState;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
export type MessageState =
|
|
203
|
+
| 'pending'
|
|
204
|
+
| 'in_flight'
|
|
205
|
+
| 'acked'
|
|
206
|
+
| 'nacked'
|
|
207
|
+
| 'dead_letter'
|
|
208
|
+
| 'expired';
|
|
209
|
+
|
|
210
|
+
export interface EnqueueAck {
|
|
211
|
+
msg_id: string;
|
|
212
|
+
queued: boolean;
|
|
213
|
+
pending: number;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
export interface DeadLetterEntry extends Message {
|
|
217
|
+
reason: string;
|
|
218
|
+
failed_at: number; // Unix seconds
|
|
219
|
+
attempts: number;
|
|
220
|
+
}
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
```typescript
|
|
224
|
+
// mailbox.ts — transport-agnostic interface
|
|
225
|
+
|
|
226
|
+
export interface MailboxProtocol {
|
|
227
|
+
enqueue(msg: Message): Promise<EnqueueAck>;
|
|
228
|
+
dequeue(sessionId: string): Promise<Message | null>;
|
|
229
|
+
ack(sessionId: string, msgId: string): Promise<void>;
|
|
230
|
+
nack(sessionId: string, msgId: string, reason: string): Promise<void>;
|
|
231
|
+
peek(sessionId: string): Promise<MessageSummary[]>;
|
|
232
|
+
purge(sessionId: string): Promise<void>;
|
|
233
|
+
peekDeadLetter(sessionId: string): Promise<DeadLetterEntry[]>;
|
|
234
|
+
purgeDeadLetter(sessionId: string): Promise<void>;
|
|
235
|
+
}
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
---
|
|
239
|
+
|
|
240
|
+
## 11. Changelog
|
|
241
|
+
|
|
242
|
+
| Version | Date | Change |
|
|
243
|
+
|---------|------|--------|
|
|
244
|
+
| 1.0-draft | 2026-04-07 | Initial protocol definition |
|