@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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dmsdc-ai/aigentry-telepty",
3
- "version": "0.1.88",
3
+ "version": "0.1.89",
4
4
  "main": "daemon.js",
5
5
  "bin": {
6
6
  "aigentry-telepty": "install.js",
@@ -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 |