swarm-mail 0.1.0 → 0.1.3
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/README.md +28 -0
- package/dist/adapter.d.ts +36 -0
- package/dist/adapter.d.ts.map +1 -0
- package/dist/index.d.ts +22 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +16710 -0
- package/{src/pglite.ts → dist/pglite.d.ts} +7 -93
- package/dist/pglite.d.ts.map +1 -0
- package/dist/streams/agent-mail.d.ts +139 -0
- package/dist/streams/agent-mail.d.ts.map +1 -0
- package/dist/streams/debug.d.ts +173 -0
- package/dist/streams/debug.d.ts.map +1 -0
- package/dist/streams/effect/ask.d.ts +124 -0
- package/dist/streams/effect/ask.d.ts.map +1 -0
- package/dist/streams/effect/cursor.d.ts +87 -0
- package/dist/streams/effect/cursor.d.ts.map +1 -0
- package/dist/streams/effect/deferred.d.ts +108 -0
- package/dist/streams/effect/deferred.d.ts.map +1 -0
- package/{src/streams/effect/index.ts → dist/streams/effect/index.d.ts} +1 -0
- package/dist/streams/effect/index.d.ts.map +1 -0
- package/{src/streams/effect/layers.ts → dist/streams/effect/layers.d.ts} +8 -33
- package/dist/streams/effect/layers.d.ts.map +1 -0
- package/dist/streams/effect/lock.d.ts +137 -0
- package/dist/streams/effect/lock.d.ts.map +1 -0
- package/dist/streams/effect/mailbox.d.ts +98 -0
- package/dist/streams/effect/mailbox.d.ts.map +1 -0
- package/dist/streams/events.d.ts +487 -0
- package/dist/streams/events.d.ts.map +1 -0
- package/dist/streams/index.d.ts +106 -0
- package/dist/streams/index.d.ts.map +1 -0
- package/dist/streams/migrations.d.ts +102 -0
- package/dist/streams/migrations.d.ts.map +1 -0
- package/dist/streams/projections.d.ts +173 -0
- package/dist/streams/projections.d.ts.map +1 -0
- package/dist/streams/store.d.ts +171 -0
- package/dist/streams/store.d.ts.map +1 -0
- package/dist/streams/swarm-mail.d.ts +153 -0
- package/dist/streams/swarm-mail.d.ts.map +1 -0
- package/dist/types/adapter.d.ts +267 -0
- package/dist/types/adapter.d.ts.map +1 -0
- package/dist/types/database.d.ts +117 -0
- package/dist/types/database.d.ts.map +1 -0
- package/{src/types/index.ts → dist/types/index.d.ts} +2 -15
- package/dist/types/index.d.ts.map +1 -0
- package/package.json +21 -5
- package/src/adapter.ts +0 -306
- package/src/index.ts +0 -57
- package/src/streams/agent-mail.test.ts +0 -777
- package/src/streams/agent-mail.ts +0 -535
- package/src/streams/debug.test.ts +0 -500
- package/src/streams/debug.ts +0 -727
- package/src/streams/effect/ask.integration.test.ts +0 -314
- package/src/streams/effect/ask.ts +0 -202
- package/src/streams/effect/cursor.integration.test.ts +0 -418
- package/src/streams/effect/cursor.ts +0 -288
- package/src/streams/effect/deferred.test.ts +0 -357
- package/src/streams/effect/deferred.ts +0 -445
- package/src/streams/effect/lock.test.ts +0 -385
- package/src/streams/effect/lock.ts +0 -399
- package/src/streams/effect/mailbox.test.ts +0 -260
- package/src/streams/effect/mailbox.ts +0 -318
- package/src/streams/events.test.ts +0 -924
- package/src/streams/events.ts +0 -329
- package/src/streams/index.test.ts +0 -229
- package/src/streams/index.ts +0 -578
- package/src/streams/migrations.test.ts +0 -359
- package/src/streams/migrations.ts +0 -362
- package/src/streams/projections.test.ts +0 -611
- package/src/streams/projections.ts +0 -564
- package/src/streams/store.integration.test.ts +0 -658
- package/src/streams/store.ts +0 -1129
- package/src/streams/swarm-mail.ts +0 -552
- package/src/types/adapter.ts +0 -392
- package/src/types/database.ts +0 -127
- package/tsconfig.json +0 -22
|
@@ -1,318 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* DurableMailbox - Actor-style messaging with envelope pattern
|
|
3
|
-
*
|
|
4
|
-
* Combines DurableCursor (positioned consumption) with Envelope pattern for
|
|
5
|
-
* request/response messaging between agents.
|
|
6
|
-
*
|
|
7
|
-
* @example
|
|
8
|
-
* ```typescript
|
|
9
|
-
* const program = Effect.gen(function* () {
|
|
10
|
-
* const mailbox = yield* DurableMailbox;
|
|
11
|
-
* const myMailbox = yield* mailbox.create({ agent: "worker-1" });
|
|
12
|
-
*
|
|
13
|
-
* // Send message with optional reply channel
|
|
14
|
-
* yield* myMailbox.send("worker-2", {
|
|
15
|
-
* payload: { task: "process-data" },
|
|
16
|
-
* replyTo: "deferred:xyz"
|
|
17
|
-
* });
|
|
18
|
-
*
|
|
19
|
-
* // Receive messages
|
|
20
|
-
* for await (const envelope of myMailbox.receive()) {
|
|
21
|
-
* yield* handleMessage(envelope.payload);
|
|
22
|
-
* if (envelope.replyTo) {
|
|
23
|
-
* yield* DurableDeferred.resolve(envelope.replyTo, result);
|
|
24
|
-
* }
|
|
25
|
-
* yield* envelope.commit();
|
|
26
|
-
* }
|
|
27
|
-
* });
|
|
28
|
-
* ```
|
|
29
|
-
*/
|
|
30
|
-
import { Context, Effect, Layer } from "effect";
|
|
31
|
-
import { DurableCursor, type Cursor } from "./cursor";
|
|
32
|
-
import { appendEvent } from "../store";
|
|
33
|
-
import type { MessageSentEvent } from "../events";
|
|
34
|
-
|
|
35
|
-
// ============================================================================
|
|
36
|
-
// Types
|
|
37
|
-
// ============================================================================
|
|
38
|
-
|
|
39
|
-
/**
|
|
40
|
-
* Envelope wrapping a message with metadata
|
|
41
|
-
*/
|
|
42
|
-
export interface Envelope<T = unknown> {
|
|
43
|
-
/** Message payload */
|
|
44
|
-
readonly payload: T;
|
|
45
|
-
/** Optional URL of DurableDeferred for response */
|
|
46
|
-
readonly replyTo?: string;
|
|
47
|
-
/** Agent who sent the message */
|
|
48
|
-
readonly sender: string;
|
|
49
|
-
/** Original message ID */
|
|
50
|
-
readonly messageId: number;
|
|
51
|
-
/** Thread ID for conversation tracking */
|
|
52
|
-
readonly threadId?: string;
|
|
53
|
-
/** Commit this message position */
|
|
54
|
-
readonly commit: () => Effect.Effect<void>;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
/**
|
|
58
|
-
* Configuration for creating a mailbox
|
|
59
|
-
*/
|
|
60
|
-
export interface MailboxConfig {
|
|
61
|
-
/** Agent name (mailbox owner) */
|
|
62
|
-
readonly agent: string;
|
|
63
|
-
/** Project key for scoping messages */
|
|
64
|
-
readonly projectKey: string;
|
|
65
|
-
/** Optional project path for database location */
|
|
66
|
-
readonly projectPath?: string;
|
|
67
|
-
/** Batch size for reading messages (default: 100) */
|
|
68
|
-
readonly batchSize?: number;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
/**
|
|
72
|
-
* Mailbox instance for an agent
|
|
73
|
-
*/
|
|
74
|
-
export interface Mailbox {
|
|
75
|
-
/** Agent name */
|
|
76
|
-
readonly agent: string;
|
|
77
|
-
/** Send a message to another agent */
|
|
78
|
-
readonly send: <T>(
|
|
79
|
-
to: string | string[],
|
|
80
|
-
envelope: {
|
|
81
|
-
payload: T;
|
|
82
|
-
replyTo?: string;
|
|
83
|
-
threadId?: string;
|
|
84
|
-
importance?: "low" | "normal" | "high" | "urgent";
|
|
85
|
-
},
|
|
86
|
-
) => Effect.Effect<void>;
|
|
87
|
-
/** Receive messages as async iterable */
|
|
88
|
-
readonly receive: <T = unknown>() => AsyncIterable<Envelope<T>>;
|
|
89
|
-
/** Peek at next message without consuming */
|
|
90
|
-
readonly peek: <T = unknown>() => Effect.Effect<Envelope<T> | null>;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
/**
|
|
94
|
-
* DurableMailbox service interface
|
|
95
|
-
*/
|
|
96
|
-
export interface DurableMailboxService {
|
|
97
|
-
/** Create a new mailbox instance */
|
|
98
|
-
readonly create: (
|
|
99
|
-
config: MailboxConfig,
|
|
100
|
-
) => Effect.Effect<Mailbox, never, DurableCursor>;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
// ============================================================================
|
|
104
|
-
// Service Definition
|
|
105
|
-
// ============================================================================
|
|
106
|
-
|
|
107
|
-
/**
|
|
108
|
-
* DurableMailbox Context.Tag
|
|
109
|
-
*/
|
|
110
|
-
export class DurableMailbox extends Context.Tag("DurableMailbox")<
|
|
111
|
-
DurableMailbox,
|
|
112
|
-
DurableMailboxService
|
|
113
|
-
>() {}
|
|
114
|
-
|
|
115
|
-
// ============================================================================
|
|
116
|
-
// Implementation
|
|
117
|
-
// ============================================================================
|
|
118
|
-
|
|
119
|
-
/**
|
|
120
|
-
* Extract envelope from MessageSentEvent
|
|
121
|
-
*/
|
|
122
|
-
function eventToEnvelope<T>(
|
|
123
|
-
event: MessageSentEvent & { id: number; sequence: number },
|
|
124
|
-
agentName: string,
|
|
125
|
-
commitFn: () => Effect.Effect<void>,
|
|
126
|
-
): Envelope<T> | null {
|
|
127
|
-
// Filter: only messages addressed to this agent
|
|
128
|
-
if (!event.to_agents.includes(agentName)) {
|
|
129
|
-
return null;
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
// Parse body as envelope (assume JSON-encoded)
|
|
133
|
-
let payload: T;
|
|
134
|
-
let replyTo: string | undefined;
|
|
135
|
-
let sender = event.from_agent;
|
|
136
|
-
|
|
137
|
-
try {
|
|
138
|
-
// Body can be either:
|
|
139
|
-
// 1. Plain JSON payload (legacy)
|
|
140
|
-
// 2. Envelope JSON with { payload, replyTo?, sender? }
|
|
141
|
-
const parsed = JSON.parse(event.body);
|
|
142
|
-
if (parsed.payload !== undefined) {
|
|
143
|
-
// It's an envelope
|
|
144
|
-
payload = parsed.payload as T;
|
|
145
|
-
replyTo = parsed.replyTo;
|
|
146
|
-
sender = parsed.sender || event.from_agent;
|
|
147
|
-
} else {
|
|
148
|
-
// It's a plain payload
|
|
149
|
-
payload = parsed as T;
|
|
150
|
-
}
|
|
151
|
-
} catch {
|
|
152
|
-
// Body is not JSON, treat as string payload
|
|
153
|
-
payload = event.body as unknown as T;
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
return {
|
|
157
|
-
payload,
|
|
158
|
-
replyTo,
|
|
159
|
-
sender,
|
|
160
|
-
messageId: event.message_id || event.id,
|
|
161
|
-
threadId: event.thread_id,
|
|
162
|
-
commit: commitFn,
|
|
163
|
-
};
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
/**
|
|
167
|
-
* Create send function
|
|
168
|
-
*/
|
|
169
|
-
function createSendFn(config: MailboxConfig): <T>(
|
|
170
|
-
to: string | string[],
|
|
171
|
-
envelope: {
|
|
172
|
-
payload: T;
|
|
173
|
-
replyTo?: string;
|
|
174
|
-
threadId?: string;
|
|
175
|
-
importance?: "low" | "normal" | "high" | "urgent";
|
|
176
|
-
},
|
|
177
|
-
) => Effect.Effect<void> {
|
|
178
|
-
return <T>(
|
|
179
|
-
to: string | string[],
|
|
180
|
-
envelope: {
|
|
181
|
-
payload: T;
|
|
182
|
-
replyTo?: string;
|
|
183
|
-
threadId?: string;
|
|
184
|
-
importance?: "low" | "normal" | "high" | "urgent";
|
|
185
|
-
},
|
|
186
|
-
): Effect.Effect<void> => {
|
|
187
|
-
return Effect.gen(function* () {
|
|
188
|
-
const toAgents = Array.isArray(to) ? to : [to];
|
|
189
|
-
|
|
190
|
-
// Create envelope body
|
|
191
|
-
const envelopeBody = {
|
|
192
|
-
payload: envelope.payload,
|
|
193
|
-
replyTo: envelope.replyTo,
|
|
194
|
-
sender: config.agent,
|
|
195
|
-
};
|
|
196
|
-
|
|
197
|
-
// Create MessageSentEvent
|
|
198
|
-
const event: Omit<
|
|
199
|
-
MessageSentEvent,
|
|
200
|
-
"id" | "sequence" | "timestamp" | "type"
|
|
201
|
-
> = {
|
|
202
|
-
project_key: config.projectKey,
|
|
203
|
-
from_agent: config.agent,
|
|
204
|
-
to_agents: toAgents,
|
|
205
|
-
subject: envelope.threadId || `msg-${Date.now()}`,
|
|
206
|
-
body: JSON.stringify(envelopeBody),
|
|
207
|
-
thread_id: envelope.threadId,
|
|
208
|
-
importance: envelope.importance || "normal",
|
|
209
|
-
ack_required: false,
|
|
210
|
-
};
|
|
211
|
-
|
|
212
|
-
// Append to event store
|
|
213
|
-
yield* Effect.promise(() =>
|
|
214
|
-
appendEvent(
|
|
215
|
-
{
|
|
216
|
-
type: "message_sent",
|
|
217
|
-
timestamp: Date.now(),
|
|
218
|
-
...event,
|
|
219
|
-
},
|
|
220
|
-
config.projectPath,
|
|
221
|
-
),
|
|
222
|
-
);
|
|
223
|
-
});
|
|
224
|
-
};
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
/**
|
|
228
|
-
* Create receive function
|
|
229
|
-
*/
|
|
230
|
-
function createReceiveFn(
|
|
231
|
-
cursor: Cursor,
|
|
232
|
-
agentName: string,
|
|
233
|
-
): <T = unknown>() => AsyncIterable<Envelope<T>> {
|
|
234
|
-
return <T = unknown>(): AsyncIterable<Envelope<T>> => {
|
|
235
|
-
const messageStream = cursor.consume<
|
|
236
|
-
MessageSentEvent & { id: number; sequence: number }
|
|
237
|
-
>();
|
|
238
|
-
|
|
239
|
-
return {
|
|
240
|
-
async *[Symbol.asyncIterator]() {
|
|
241
|
-
for await (const msg of messageStream) {
|
|
242
|
-
const envelope = eventToEnvelope<T>(msg.value, agentName, msg.commit);
|
|
243
|
-
if (envelope) {
|
|
244
|
-
yield envelope;
|
|
245
|
-
} else {
|
|
246
|
-
// Not for this agent, skip and commit
|
|
247
|
-
await Effect.runPromise(msg.commit());
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
},
|
|
251
|
-
};
|
|
252
|
-
};
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
/**
|
|
256
|
-
* Create peek function
|
|
257
|
-
*/
|
|
258
|
-
function createPeekFn(
|
|
259
|
-
cursor: Cursor,
|
|
260
|
-
agentName: string,
|
|
261
|
-
): <T = unknown>() => Effect.Effect<Envelope<T> | null> {
|
|
262
|
-
return <T = unknown>(): Effect.Effect<Envelope<T> | null> => {
|
|
263
|
-
return Effect.promise(async () => {
|
|
264
|
-
const messageStream = cursor.consume<
|
|
265
|
-
MessageSentEvent & { id: number; sequence: number }
|
|
266
|
-
>();
|
|
267
|
-
|
|
268
|
-
for await (const msg of messageStream) {
|
|
269
|
-
const envelope = eventToEnvelope<T>(msg.value, agentName, msg.commit);
|
|
270
|
-
if (envelope) {
|
|
271
|
-
return envelope;
|
|
272
|
-
}
|
|
273
|
-
// Not for this agent, skip and commit
|
|
274
|
-
await Effect.runPromise(msg.commit());
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
return null;
|
|
278
|
-
});
|
|
279
|
-
};
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
/**
|
|
283
|
-
* Create mailbox implementation
|
|
284
|
-
*/
|
|
285
|
-
function createMailboxImpl(
|
|
286
|
-
config: MailboxConfig,
|
|
287
|
-
): Effect.Effect<Mailbox, never, DurableCursor> {
|
|
288
|
-
return Effect.gen(function* () {
|
|
289
|
-
const cursorService = yield* DurableCursor;
|
|
290
|
-
|
|
291
|
-
// Create cursor for this agent's messages
|
|
292
|
-
const cursor = yield* cursorService.create({
|
|
293
|
-
stream: `projects/${config.projectKey}/events`,
|
|
294
|
-
checkpoint: `agents/${config.agent}/mailbox`,
|
|
295
|
-
projectPath: config.projectPath,
|
|
296
|
-
batchSize: config.batchSize,
|
|
297
|
-
types: ["message_sent"], // Only read message_sent events
|
|
298
|
-
});
|
|
299
|
-
|
|
300
|
-
return {
|
|
301
|
-
agent: config.agent,
|
|
302
|
-
send: createSendFn(config),
|
|
303
|
-
receive: createReceiveFn(cursor, config.agent),
|
|
304
|
-
peek: createPeekFn(cursor, config.agent),
|
|
305
|
-
};
|
|
306
|
-
});
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
// ============================================================================
|
|
310
|
-
// Layer
|
|
311
|
-
// ============================================================================
|
|
312
|
-
|
|
313
|
-
/**
|
|
314
|
-
* Live implementation of DurableMailbox service
|
|
315
|
-
*/
|
|
316
|
-
export const DurableMailboxLive = Layer.succeed(DurableMailbox, {
|
|
317
|
-
create: createMailboxImpl,
|
|
318
|
-
});
|