agent-inbox 0.2.4 → 0.2.5
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 +1 -92
- package/README.md +6 -73
- package/dist/federation/connection-manager.d.ts +0 -8
- package/dist/federation/connection-manager.d.ts.map +1 -1
- package/dist/federation/connection-manager.js +0 -12
- package/dist/federation/connection-manager.js.map +1 -1
- package/dist/federation/delivery-queue.d.ts +3 -11
- package/dist/federation/delivery-queue.d.ts.map +1 -1
- package/dist/federation/delivery-queue.js +8 -38
- package/dist/federation/delivery-queue.js.map +1 -1
- package/dist/index.d.ts +0 -17
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +0 -98
- package/dist/index.js.map +1 -1
- package/dist/jsonrpc/mail-push-types.d.ts +22 -2
- package/dist/jsonrpc/mail-push-types.d.ts.map +1 -1
- package/dist/jsonrpc/mail-push-types.js +18 -1
- package/dist/jsonrpc/mail-push-types.js.map +1 -1
- package/dist/jsonrpc/mail-push.d.ts +12 -1
- package/dist/jsonrpc/mail-push.d.ts.map +1 -1
- package/dist/jsonrpc/mail-push.js +13 -2
- package/dist/jsonrpc/mail-push.js.map +1 -1
- package/dist/jsonrpc/mail-server.d.ts.map +1 -1
- package/dist/jsonrpc/mail-server.js +42 -18
- package/dist/jsonrpc/mail-server.js.map +1 -1
- package/dist/router/message-router.d.ts +0 -15
- package/dist/router/message-router.d.ts.map +1 -1
- package/dist/router/message-router.js +3 -25
- package/dist/router/message-router.js.map +1 -1
- package/dist/storage/interface.d.ts +2 -9
- package/dist/storage/interface.d.ts.map +1 -1
- package/dist/storage/memory.d.ts +1 -4
- package/dist/storage/memory.d.ts.map +1 -1
- package/dist/storage/memory.js +6 -12
- package/dist/storage/memory.js.map +1 -1
- package/dist/storage/sqlite.d.ts +1 -6
- package/dist/storage/sqlite.d.ts.map +1 -1
- package/dist/storage/sqlite.js +6 -28
- package/dist/storage/sqlite.js.map +1 -1
- package/dist/types.d.ts +0 -79
- package/dist/types.d.ts.map +1 -1
- package/docs/DESIGN.md +0 -15
- package/package.json +3 -28
- package/rules/agent-inbox.md +0 -1
- package/src/federation/connection-manager.ts +0 -12
- package/src/federation/delivery-queue.ts +8 -38
- package/src/index.ts +0 -148
- package/src/jsonrpc/mail-push-types.ts +43 -2
- package/src/jsonrpc/mail-push.ts +34 -3
- package/src/jsonrpc/mail-server.ts +45 -26
- package/src/router/message-router.ts +4 -41
- package/src/storage/interface.ts +2 -11
- package/src/storage/memory.ts +8 -15
- package/src/storage/sqlite.ts +9 -36
- package/src/types.ts +0 -73
- package/test/load.test.ts +1 -1
- package/test/mail-push.test.ts +101 -1
- package/test/mail-server.test.ts +108 -0
- package/AGENTS.md +0 -18
- package/dist/federation/queue-store.d.ts +0 -42
- package/dist/federation/queue-store.d.ts.map +0 -1
- package/dist/federation/queue-store.js +0 -87
- package/dist/federation/queue-store.js.map +0 -1
- package/dist/index.d.mts +0 -2
- package/dist/index.mjs +0 -1
- package/dist/index.mjs.map +0 -1
- package/dist/mail/address-book.d.ts +0 -43
- package/dist/mail/address-book.d.ts.map +0 -1
- package/dist/mail/address-book.js +0 -95
- package/dist/mail/address-book.js.map +0 -1
- package/dist/mail/attachment-store.d.ts +0 -31
- package/dist/mail/attachment-store.d.ts.map +0 -1
- package/dist/mail/attachment-store.js +0 -74
- package/dist/mail/attachment-store.js.map +0 -1
- package/dist/mail/email-mapper.d.ts +0 -41
- package/dist/mail/email-mapper.d.ts.map +0 -1
- package/dist/mail/email-mapper.js +0 -216
- package/dist/mail/email-mapper.js.map +0 -1
- package/dist/mail/fs-attachment-store.d.ts +0 -38
- package/dist/mail/fs-attachment-store.d.ts.map +0 -1
- package/dist/mail/fs-attachment-store.js +0 -165
- package/dist/mail/fs-attachment-store.js.map +0 -1
- package/dist/mail/mail-gateway.d.ts +0 -114
- package/dist/mail/mail-gateway.d.ts.map +0 -1
- package/dist/mail/mail-gateway.js +0 -402
- package/dist/mail/mail-gateway.js.map +0 -1
- package/dist/mail/provider-transport.d.ts +0 -138
- package/dist/mail/provider-transport.d.ts.map +0 -1
- package/dist/mail/provider-transport.js +0 -434
- package/dist/mail/provider-transport.js.map +0 -1
- package/dist/mail/rate-limiter.d.ts +0 -20
- package/dist/mail/rate-limiter.d.ts.map +0 -1
- package/dist/mail/rate-limiter.js +0 -56
- package/dist/mail/rate-limiter.js.map +0 -1
- package/dist/mail/smtp-transport.d.ts +0 -141
- package/dist/mail/smtp-transport.d.ts.map +0 -1
- package/dist/mail/smtp-transport.js +0 -415
- package/dist/mail/smtp-transport.js.map +0 -1
- package/dist/mail/types.d.ts +0 -177
- package/dist/mail/types.d.ts.map +0 -1
- package/dist/mail/types.js +0 -11
- package/dist/mail/types.js.map +0 -1
- package/dist/router/destination.d.ts +0 -69
- package/dist/router/destination.d.ts.map +0 -1
- package/dist/router/destination.js +0 -106
- package/dist/router/destination.js.map +0 -1
- package/docs/MAIL-INTEROP-PLAN.md +0 -660
- package/renovate.json5 +0 -6
- package/src/federation/queue-store.ts +0 -124
- package/src/mail/address-book.ts +0 -111
- package/src/mail/attachment-store.ts +0 -90
- package/src/mail/email-mapper.ts +0 -288
- package/src/mail/fs-attachment-store.ts +0 -163
- package/src/mail/mail-gateway.ts +0 -505
- package/src/mail/provider-transport.ts +0 -577
- package/src/mail/rate-limiter.ts +0 -51
- package/src/mail/smtp-transport.ts +0 -589
- package/src/mail/types.ts +0 -221
- package/src/router/destination.ts +0 -140
- package/test/federation/delivery-queue-sqlite.test.ts +0 -158
- package/test/mail/address-book.test.ts +0 -111
- package/test/mail/attachment-store-contract.test.ts +0 -92
- package/test/mail/attachment-store.test.ts +0 -69
- package/test/mail/destination.test.ts +0 -115
- package/test/mail/dsn-parse.test.ts +0 -239
- package/test/mail/email-mapper.test.ts +0 -341
- package/test/mail/external-id.test.ts +0 -43
- package/test/mail/fs-attachment-store.test.ts +0 -134
- package/test/mail/full-flow-e2e.test.ts +0 -200
- package/test/mail/mail-gateway.test.ts +0 -419
- package/test/mail/mail-transport-contract.test.ts +0 -134
- package/test/mail/mock-mail.ts +0 -161
- package/test/mail/mock-postmark.ts +0 -66
- package/test/mail/provider-transport.test.ts +0 -381
- package/test/mail/rate-limiter.test.ts +0 -48
- package/test/mail/router-mail-integration.test.ts +0 -138
- package/test/mail/smtp-e2e.test.ts +0 -98
- package/test/mail/smtp-transport.test.ts +0 -138
package/src/storage/interface.ts
CHANGED
|
@@ -36,17 +36,6 @@ export interface Storage {
|
|
|
36
36
|
/** Delete messages older than `cutoff` (ISO timestamp). Returns the count removed. */
|
|
37
37
|
pruneMessagesOlderThan(cutoff: string): number;
|
|
38
38
|
|
|
39
|
-
// External-id mapping (mail dedup + bounce correlation)
|
|
40
|
-
/**
|
|
41
|
-
* Record that an external message id (e.g. an RFC 5322 Message-ID) maps to a
|
|
42
|
-
* stored inbox message. Idempotent: re-recording the same external id is a no-op.
|
|
43
|
-
*/
|
|
44
|
-
recordExternalId(externalId: string, messageId: string): void;
|
|
45
|
-
/** Look up the inbox message id previously recorded for an external id. */
|
|
46
|
-
getMessageIdByExternalId(externalId: string): string | undefined;
|
|
47
|
-
/** Convenience: whether an external id has already been recorded. */
|
|
48
|
-
hasSeenExternalId(externalId: string): boolean;
|
|
49
|
-
|
|
50
39
|
// Conversations
|
|
51
40
|
getConversation(id: string): Conversation | undefined;
|
|
52
41
|
putConversation(conversation: Conversation): Conversation;
|
|
@@ -57,6 +46,8 @@ export interface Storage {
|
|
|
57
46
|
conversationId: string,
|
|
58
47
|
participant: { agent_id: string; role?: string; joined_at: string }
|
|
59
48
|
): void;
|
|
49
|
+
/** Remove a participant by agent id. No-op if not present. */
|
|
50
|
+
removeParticipant(conversationId: string, agentId: string): void;
|
|
60
51
|
listConversations(scope?: string): Conversation[];
|
|
61
52
|
|
|
62
53
|
// Turns
|
package/src/storage/memory.ts
CHANGED
|
@@ -13,7 +13,6 @@ export class InMemoryStorage implements Storage {
|
|
|
13
13
|
private conversations = new Map<string, Conversation>();
|
|
14
14
|
private turns: Turn[] = [];
|
|
15
15
|
private threads = new Map<string, Thread>();
|
|
16
|
-
private externalIds = new Map<string, string>();
|
|
17
16
|
|
|
18
17
|
// --- Agents ---
|
|
19
18
|
|
|
@@ -51,20 +50,6 @@ export class InMemoryStorage implements Storage {
|
|
|
51
50
|
if (msg) msg.conversation_id = conversationId;
|
|
52
51
|
}
|
|
53
52
|
|
|
54
|
-
recordExternalId(externalId: string, messageId: string): void {
|
|
55
|
-
if (!this.externalIds.has(externalId)) {
|
|
56
|
-
this.externalIds.set(externalId, messageId);
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
getMessageIdByExternalId(externalId: string): string | undefined {
|
|
61
|
-
return this.externalIds.get(externalId);
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
hasSeenExternalId(externalId: string): boolean {
|
|
65
|
-
return this.externalIds.has(externalId);
|
|
66
|
-
}
|
|
67
|
-
|
|
68
53
|
pruneMessagesOlderThan(cutoff: string): number {
|
|
69
54
|
let removed = 0;
|
|
70
55
|
const removedIds = new Set<string>();
|
|
@@ -167,6 +152,14 @@ export class InMemoryStorage implements Storage {
|
|
|
167
152
|
});
|
|
168
153
|
}
|
|
169
154
|
|
|
155
|
+
removeParticipant(conversationId: string, agentId: string): void {
|
|
156
|
+
const conv = this.conversations.get(conversationId);
|
|
157
|
+
if (!conv) return;
|
|
158
|
+
conv.participants = conv.participants.filter(
|
|
159
|
+
(p) => p.agent_id !== agentId
|
|
160
|
+
);
|
|
161
|
+
}
|
|
162
|
+
|
|
170
163
|
listConversations(scope?: string): Conversation[] {
|
|
171
164
|
const all = Array.from(this.conversations.values());
|
|
172
165
|
if (!scope) return all;
|
package/src/storage/sqlite.ts
CHANGED
|
@@ -128,13 +128,6 @@ export class SqliteStorage implements Storage {
|
|
|
128
128
|
FOREIGN KEY (conversation_id) REFERENCES ${t("conversations")}(id)
|
|
129
129
|
);
|
|
130
130
|
|
|
131
|
-
-- External-id mapping (mail dedup + bounce correlation)
|
|
132
|
-
CREATE TABLE IF NOT EXISTS ${t("external_ids")} (
|
|
133
|
-
external_id TEXT PRIMARY KEY,
|
|
134
|
-
message_id TEXT NOT NULL,
|
|
135
|
-
recorded_at TEXT NOT NULL
|
|
136
|
-
);
|
|
137
|
-
|
|
138
131
|
-- Indexes for common queries
|
|
139
132
|
CREATE INDEX IF NOT EXISTS idx_${t("messages")}_scope ON ${t("messages")}(scope);
|
|
140
133
|
CREATE INDEX IF NOT EXISTS idx_${t("messages")}_sender ON ${t("messages")}(sender_id);
|
|
@@ -190,12 +183,6 @@ export class SqliteStorage implements Storage {
|
|
|
190
183
|
`);
|
|
191
184
|
}
|
|
192
185
|
|
|
193
|
-
/** Expose the underlying handle so sibling features (mail queue/attachments)
|
|
194
|
-
* can co-locate their tables. Caller must not close it. */
|
|
195
|
-
getDatabase(): Database.Database {
|
|
196
|
-
return this.db;
|
|
197
|
-
}
|
|
198
|
-
|
|
199
186
|
// --- Agents ---
|
|
200
187
|
|
|
201
188
|
getAgent(agentId: string): Agent | undefined {
|
|
@@ -304,29 +291,6 @@ export class SqliteStorage implements Storage {
|
|
|
304
291
|
.run(conversationId, messageId);
|
|
305
292
|
}
|
|
306
293
|
|
|
307
|
-
recordExternalId(externalId: string, messageId: string): void {
|
|
308
|
-
this.db
|
|
309
|
-
.prepare(
|
|
310
|
-
`INSERT INTO ${this.p("external_ids")} (external_id, message_id, recorded_at)
|
|
311
|
-
VALUES (?, ?, ?)
|
|
312
|
-
ON CONFLICT(external_id) DO NOTHING`
|
|
313
|
-
)
|
|
314
|
-
.run(externalId, messageId, new Date().toISOString());
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
getMessageIdByExternalId(externalId: string): string | undefined {
|
|
318
|
-
const row = this.db
|
|
319
|
-
.prepare(
|
|
320
|
-
`SELECT message_id FROM ${this.p("external_ids")} WHERE external_id = ?`
|
|
321
|
-
)
|
|
322
|
-
.get(externalId) as { message_id: string } | undefined;
|
|
323
|
-
return row?.message_id;
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
hasSeenExternalId(externalId: string): boolean {
|
|
327
|
-
return this.getMessageIdByExternalId(externalId) !== undefined;
|
|
328
|
-
}
|
|
329
|
-
|
|
330
294
|
pruneMessagesOlderThan(cutoff: string): number {
|
|
331
295
|
const m = this.p("messages");
|
|
332
296
|
const r = this.p("recipients");
|
|
@@ -527,6 +491,15 @@ export class SqliteStorage implements Storage {
|
|
|
527
491
|
);
|
|
528
492
|
}
|
|
529
493
|
|
|
494
|
+
removeParticipant(conversationId: string, agentId: string): void {
|
|
495
|
+
this.db
|
|
496
|
+
.prepare(
|
|
497
|
+
`DELETE FROM ${this.p("participants")}
|
|
498
|
+
WHERE conversation_id = ? AND agent_id = ?`
|
|
499
|
+
)
|
|
500
|
+
.run(conversationId, agentId);
|
|
501
|
+
}
|
|
502
|
+
|
|
530
503
|
listConversations(scope?: string): Conversation[] {
|
|
531
504
|
let rows: ConversationRow[];
|
|
532
505
|
if (scope) {
|
package/src/types.ts
CHANGED
|
@@ -211,11 +211,6 @@ export interface FederationPeerConfig {
|
|
|
211
211
|
url?: string;
|
|
212
212
|
/** Mesh peer ID for agentic-mesh transport connections. */
|
|
213
213
|
meshPeerId?: string;
|
|
214
|
-
/**
|
|
215
|
-
* Dotted addresses (e.g. "team.corp.internal") routed to this peer rather
|
|
216
|
-
* than treated as external mail. See destination classification (§1).
|
|
217
|
-
*/
|
|
218
|
-
domains?: string[];
|
|
219
214
|
auth?: FederationAuth;
|
|
220
215
|
exposure?: ExposurePolicy;
|
|
221
216
|
}
|
|
@@ -316,73 +311,6 @@ export interface QueuedMessage {
|
|
|
316
311
|
nextRetry?: string;
|
|
317
312
|
}
|
|
318
313
|
|
|
319
|
-
// --- Mail interop ---
|
|
320
|
-
|
|
321
|
-
/** Maps between agent ids and email addresses; declares owned domains. */
|
|
322
|
-
export interface MailIdentityConfig {
|
|
323
|
-
/** Domains we receive for; inbound RCPT TO outside these is rejected. */
|
|
324
|
-
localDomains: string[];
|
|
325
|
-
/** agent_id ↔ primary email. Plus-addressing (agent+scope@domain) aware. */
|
|
326
|
-
mappings: Array<{ agentId: string; address: string }>;
|
|
327
|
-
/** Default mailbox for accepted-but-unmatched inbound (e.g. a triage agent). */
|
|
328
|
-
catchAllAgentId?: string;
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
export interface MailConfig {
|
|
332
|
-
enabled: boolean;
|
|
333
|
-
backend: "smtp" | "provider";
|
|
334
|
-
identity: MailIdentityConfig;
|
|
335
|
-
/**
|
|
336
|
-
* Domains we will SEND to without erroring (destination classification §1).
|
|
337
|
-
* Receiving uses identity.localDomains. Anything outside both → error.
|
|
338
|
-
*/
|
|
339
|
-
routableDomains?: string[];
|
|
340
|
-
smtp?: {
|
|
341
|
-
listenPort?: number;
|
|
342
|
-
relay?: {
|
|
343
|
-
host: string;
|
|
344
|
-
port: number;
|
|
345
|
-
auth?: { user: string; pass: string };
|
|
346
|
-
};
|
|
347
|
-
dkim?: { domain: string; selector: string; privateKeyRef: string };
|
|
348
|
-
};
|
|
349
|
-
provider?: {
|
|
350
|
-
name: "ses" | "postmark" | "mailgun";
|
|
351
|
-
apiKeyRef: string;
|
|
352
|
-
webhookPath?: string;
|
|
353
|
-
};
|
|
354
|
-
/** Delivery queue overrides; defaults to sqlite persistence when mail enabled. */
|
|
355
|
-
queue?: Partial<DeliveryQueueConfig>;
|
|
356
|
-
/**
|
|
357
|
-
* Attachment byte storage. Defaults to "sqlite" (content-addressed BLOBs in
|
|
358
|
-
* the DB). Use "fs" to keep bytes on disk for large/high-volume deployments.
|
|
359
|
-
*/
|
|
360
|
-
attachments?: {
|
|
361
|
-
backend?: "sqlite" | "fs";
|
|
362
|
-
/** Directory for the "fs" backend. Defaults to <home>/.claude/agent-inbox/attachments. */
|
|
363
|
-
dir?: string;
|
|
364
|
-
};
|
|
365
|
-
/** Inbound sender allow-list (domains). Empty/undefined = allow all. */
|
|
366
|
-
allowedSenderDomains?: string[];
|
|
367
|
-
/** Drop inbound whose DMARC verdict is "fail". */
|
|
368
|
-
rejectDmarcFail?: boolean;
|
|
369
|
-
/** Max attachments on an inbound message before rejection. */
|
|
370
|
-
maxAttachments?: number;
|
|
371
|
-
/** Inbound rate limiting (abuse control). Disabled if unset. */
|
|
372
|
-
rateLimit?: {
|
|
373
|
-
windowMs?: number;
|
|
374
|
-
perSenderDomain?: number;
|
|
375
|
-
global?: number;
|
|
376
|
-
};
|
|
377
|
-
/** Bounce handling (both default on). See §8. */
|
|
378
|
-
bounce?: {
|
|
379
|
-
/** Emit the mail.bounced event. */
|
|
380
|
-
emitEvent?: boolean;
|
|
381
|
-
/** Inject a synthetic bounce Message into the original sender's inbox. */
|
|
382
|
-
synthesizeInboxMessage?: boolean;
|
|
383
|
-
};
|
|
384
|
-
}
|
|
385
|
-
|
|
386
314
|
// --- Config ---
|
|
387
315
|
|
|
388
316
|
export interface InboxConfig {
|
|
@@ -399,5 +327,4 @@ export interface InboxConfig {
|
|
|
399
327
|
};
|
|
400
328
|
};
|
|
401
329
|
federation?: FederationConfig;
|
|
402
|
-
mail?: MailConfig;
|
|
403
330
|
}
|
package/test/load.test.ts
CHANGED
|
@@ -252,7 +252,7 @@ describe("load: SQLite stays bounded", () => {
|
|
|
252
252
|
// 5000 messages × 11 recipients ≈ 55k recipient rows + 5k turns + FTS.
|
|
253
253
|
// 4 KiB per logical message is a generous ceiling.
|
|
254
254
|
expect(size).toBeLessThan(N * 4096);
|
|
255
|
-
}
|
|
255
|
+
});
|
|
256
256
|
|
|
257
257
|
it("pruneMessagesOlderThan should remove old data and reduce row counts", async () => {
|
|
258
258
|
const storage = new SqliteStorage({ path: ":memory:" });
|
package/test/mail-push.test.ts
CHANGED
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
buildMailTurnReceivedParams,
|
|
12
12
|
type MailPushSubscriber,
|
|
13
13
|
} from "../src/index.js";
|
|
14
|
-
import type { Turn } from "../src/types.js";
|
|
14
|
+
import type { Turn, Conversation } from "../src/types.js";
|
|
15
15
|
|
|
16
16
|
function makeTurn(overrides: Partial<Turn> = {}): Turn {
|
|
17
17
|
return {
|
|
@@ -25,6 +25,23 @@ function makeTurn(overrides: Partial<Turn> = {}): Turn {
|
|
|
25
25
|
};
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
+
function makeConversation(overrides: Partial<Conversation> = {}): Conversation {
|
|
29
|
+
return {
|
|
30
|
+
id: "conv-1",
|
|
31
|
+
scope: "spec-thread",
|
|
32
|
+
subject: "Spec: Auth",
|
|
33
|
+
status: "active",
|
|
34
|
+
participants: [
|
|
35
|
+
{ agent_id: "codex-1", role: "reviewer", joined_at: "2026-05-04T00:00:00.000Z" },
|
|
36
|
+
{ agent_id: "user-1", role: "initiator", joined_at: "2026-05-04T00:00:00.000Z" },
|
|
37
|
+
],
|
|
38
|
+
metadata: { spec_id: "spec-1" },
|
|
39
|
+
created_at: "2026-05-04T00:00:00.000Z",
|
|
40
|
+
updated_at: "2026-05-04T00:00:00.000Z",
|
|
41
|
+
...overrides,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
|
|
28
45
|
describe("createMailPushBridge", () => {
|
|
29
46
|
it("subscribes to mail.turn.added on construction", () => {
|
|
30
47
|
const events = new EventEmitter();
|
|
@@ -232,6 +249,66 @@ describe("createMailPushBridge", () => {
|
|
|
232
249
|
expect(params).not.toHaveProperty("importance");
|
|
233
250
|
});
|
|
234
251
|
|
|
252
|
+
it("folds conversation context into params when resolveConversation is provided", () => {
|
|
253
|
+
const events = new EventEmitter();
|
|
254
|
+
const sendNotification = vi.fn();
|
|
255
|
+
|
|
256
|
+
createMailPushBridge({
|
|
257
|
+
mailEvents: events,
|
|
258
|
+
getSubscribers: () => [{ id: "swarm-1" }],
|
|
259
|
+
sendNotification,
|
|
260
|
+
resolveConversation: () => makeConversation(),
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
events.emit("mail.turn.added", makeTurn());
|
|
264
|
+
expect(sendNotification).toHaveBeenCalledOnce();
|
|
265
|
+
const params = sendNotification.mock.calls[0][2];
|
|
266
|
+
expect(params.conversation).toEqual({
|
|
267
|
+
scope: "spec-thread",
|
|
268
|
+
subject: "Spec: Auth",
|
|
269
|
+
metadata: { spec_id: "spec-1" },
|
|
270
|
+
participants: ["codex-1", "user-1"],
|
|
271
|
+
});
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
it("omits the conversation block when resolveConversation returns undefined", () => {
|
|
275
|
+
const events = new EventEmitter();
|
|
276
|
+
const sendNotification = vi.fn();
|
|
277
|
+
|
|
278
|
+
createMailPushBridge({
|
|
279
|
+
mailEvents: events,
|
|
280
|
+
getSubscribers: () => [{ id: "swarm-1" }],
|
|
281
|
+
sendNotification,
|
|
282
|
+
resolveConversation: () => undefined,
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
events.emit("mail.turn.added", makeTurn());
|
|
286
|
+
const params = sendNotification.mock.calls[0][2];
|
|
287
|
+
expect(params).not.toHaveProperty("conversation");
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
it("degrades to a context-less notification when resolveConversation throws", () => {
|
|
291
|
+
const events = new EventEmitter();
|
|
292
|
+
const logs: string[] = [];
|
|
293
|
+
const sendNotification = vi.fn();
|
|
294
|
+
|
|
295
|
+
createMailPushBridge({
|
|
296
|
+
mailEvents: events,
|
|
297
|
+
getSubscribers: () => [{ id: "swarm-1" }],
|
|
298
|
+
sendNotification,
|
|
299
|
+
resolveConversation: () => {
|
|
300
|
+
throw new Error("storage down");
|
|
301
|
+
},
|
|
302
|
+
log: (msg) => logs.push(msg),
|
|
303
|
+
});
|
|
304
|
+
|
|
305
|
+
events.emit("mail.turn.added", makeTurn());
|
|
306
|
+
// Notification still sent, just without conversation context.
|
|
307
|
+
expect(sendNotification).toHaveBeenCalledOnce();
|
|
308
|
+
expect(sendNotification.mock.calls[0][2]).not.toHaveProperty("conversation");
|
|
309
|
+
expect(logs.some((l) => l.includes("resolveConversation threw"))).toBe(true);
|
|
310
|
+
});
|
|
311
|
+
|
|
235
312
|
it("stop() is idempotent", () => {
|
|
236
313
|
const events = new EventEmitter();
|
|
237
314
|
const bridge = createMailPushBridge({
|
|
@@ -255,4 +332,27 @@ describe("buildMailTurnReceivedParams", () => {
|
|
|
255
332
|
const params = buildMailTurnReceivedParams(makeTurn());
|
|
256
333
|
expect(params).not.toHaveProperty("importance");
|
|
257
334
|
});
|
|
335
|
+
|
|
336
|
+
it("maps conversation context when a conversation is passed", () => {
|
|
337
|
+
const params = buildMailTurnReceivedParams(makeTurn(), makeConversation());
|
|
338
|
+
expect(params.conversation).toEqual({
|
|
339
|
+
scope: "spec-thread",
|
|
340
|
+
subject: "Spec: Auth",
|
|
341
|
+
metadata: { spec_id: "spec-1" },
|
|
342
|
+
participants: ["codex-1", "user-1"],
|
|
343
|
+
});
|
|
344
|
+
});
|
|
345
|
+
|
|
346
|
+
it("omits subject in conversation context when the conversation has none", () => {
|
|
347
|
+
const params = buildMailTurnReceivedParams(
|
|
348
|
+
makeTurn(),
|
|
349
|
+
makeConversation({ subject: undefined })
|
|
350
|
+
);
|
|
351
|
+
expect(params.conversation).not.toHaveProperty("subject");
|
|
352
|
+
});
|
|
353
|
+
|
|
354
|
+
it("omits the conversation block entirely when no conversation is passed", () => {
|
|
355
|
+
const params = buildMailTurnReceivedParams(makeTurn());
|
|
356
|
+
expect(params).not.toHaveProperty("conversation");
|
|
357
|
+
});
|
|
258
358
|
});
|
package/test/mail-server.test.ts
CHANGED
|
@@ -34,6 +34,55 @@ describe("MailJsonRpcServer", () => {
|
|
|
34
34
|
expect(conv.subject).toBe("Test conv");
|
|
35
35
|
expect(conv.id).toMatch(/^conv-/);
|
|
36
36
|
});
|
|
37
|
+
|
|
38
|
+
it("is create-or-get: returns the existing conversation for a repeated explicit id", async () => {
|
|
39
|
+
const first = await rpc("mail/create", {
|
|
40
|
+
id: "spec-thread:res-1:spec-1",
|
|
41
|
+
subject: "Spec: Auth",
|
|
42
|
+
scope: "spec-thread",
|
|
43
|
+
metadata: { spec_id: "spec-1" },
|
|
44
|
+
});
|
|
45
|
+
const firstConv = first.result as { id: string };
|
|
46
|
+
|
|
47
|
+
// Invite a participant, then re-create with the same id.
|
|
48
|
+
await rpc("mail/invite", {
|
|
49
|
+
conversationId: firstConv.id,
|
|
50
|
+
agentId: "codex-1",
|
|
51
|
+
role: "reviewer",
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
const second = await rpc("mail/create", {
|
|
55
|
+
id: "spec-thread:res-1:spec-1",
|
|
56
|
+
subject: "Different subject that must be ignored",
|
|
57
|
+
scope: "spec-thread",
|
|
58
|
+
metadata: { spec_id: "OVERWRITE-ME" },
|
|
59
|
+
});
|
|
60
|
+
const secondConv = second.result as {
|
|
61
|
+
id: string;
|
|
62
|
+
subject: string;
|
|
63
|
+
participants: unknown[];
|
|
64
|
+
metadata: Record<string, unknown>;
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
// Same conversation returned untouched — no participant wipe, no
|
|
68
|
+
// metadata/subject overwrite.
|
|
69
|
+
expect(secondConv.id).toBe(firstConv.id);
|
|
70
|
+
expect(secondConv.subject).toBe("Spec: Auth");
|
|
71
|
+
expect(secondConv.metadata.spec_id).toBe("spec-1");
|
|
72
|
+
expect(secondConv.participants).toHaveLength(1);
|
|
73
|
+
|
|
74
|
+
const stored = storage.getConversation(firstConv.id)!;
|
|
75
|
+
expect(stored.participants).toHaveLength(1);
|
|
76
|
+
expect(stored.participants[0].agent_id).toBe("codex-1");
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
it("does not emit mail.created when returning an existing conversation", async () => {
|
|
80
|
+
const listener = vi.fn();
|
|
81
|
+
await rpc("mail/create", { id: "conv-fixed", subject: "First" });
|
|
82
|
+
events.on("mail.created", listener);
|
|
83
|
+
await rpc("mail/create", { id: "conv-fixed", subject: "Second" });
|
|
84
|
+
expect(listener).not.toHaveBeenCalled();
|
|
85
|
+
});
|
|
37
86
|
});
|
|
38
87
|
|
|
39
88
|
describe("mail/get", () => {
|
|
@@ -132,6 +181,33 @@ describe("MailJsonRpcServer", () => {
|
|
|
132
181
|
conv = storage.getConversation(convId)!;
|
|
133
182
|
expect(conv.participants).toHaveLength(0);
|
|
134
183
|
});
|
|
184
|
+
|
|
185
|
+
it("mail/leave removes only the target, preserving other participants", async () => {
|
|
186
|
+
const createResp = await rpc("mail/create", { subject: "Test" });
|
|
187
|
+
const convId = (createResp.result as { id: string }).id;
|
|
188
|
+
|
|
189
|
+
await rpc("mail/join", { conversationId: convId, agentId: "alice" });
|
|
190
|
+
await rpc("mail/join", { conversationId: convId, agentId: "bob" });
|
|
191
|
+
|
|
192
|
+
await rpc("mail/leave", { conversationId: convId, agentId: "alice" });
|
|
193
|
+
const conv = storage.getConversation(convId)!;
|
|
194
|
+
expect(conv.participants.map((p) => p.agent_id)).toEqual(["bob"]);
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
it("mail/leave is a no-op for a non-participant", async () => {
|
|
198
|
+
const createResp = await rpc("mail/create", { subject: "Test" });
|
|
199
|
+
const convId = (createResp.result as { id: string }).id;
|
|
200
|
+
await rpc("mail/join", { conversationId: convId, agentId: "bob" });
|
|
201
|
+
|
|
202
|
+
const resp = await rpc("mail/leave", { conversationId: convId, agentId: "ghost" });
|
|
203
|
+
expect((resp.result as { ok: boolean }).ok).toBe(true);
|
|
204
|
+
expect(storage.getConversation(convId)!.participants).toHaveLength(1);
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
it("mail/leave errors on a missing conversation", async () => {
|
|
208
|
+
const resp = await rpc("mail/leave", { conversationId: "nope", agentId: "alice" });
|
|
209
|
+
expect(resp.error?.code).toBe(-32001);
|
|
210
|
+
});
|
|
135
211
|
});
|
|
136
212
|
|
|
137
213
|
describe("mail/invite", () => {
|
|
@@ -145,6 +221,38 @@ describe("MailJsonRpcServer", () => {
|
|
|
145
221
|
expect(conv.participants[0].agent_id).toBe("bob");
|
|
146
222
|
expect(conv.participants[0].role).toBe("reviewer");
|
|
147
223
|
});
|
|
224
|
+
|
|
225
|
+
it("is idempotent and preserves other participants", async () => {
|
|
226
|
+
const createResp = await rpc("mail/create", { subject: "Test" });
|
|
227
|
+
const convId = (createResp.result as { id: string }).id;
|
|
228
|
+
|
|
229
|
+
await rpc("mail/invite", { conversationId: convId, agentId: "alice" });
|
|
230
|
+
await rpc("mail/invite", { conversationId: convId, agentId: "bob", role: "reviewer" });
|
|
231
|
+
// Duplicate invite — no-op, does not disturb the existing list.
|
|
232
|
+
await rpc("mail/invite", { conversationId: convId, agentId: "bob", role: "editor" });
|
|
233
|
+
|
|
234
|
+
const conv = storage.getConversation(convId)!;
|
|
235
|
+
expect(conv.participants.map((p) => p.agent_id)).toEqual(["alice", "bob"]);
|
|
236
|
+
// Role from the first invite is retained (duplicate is a no-op).
|
|
237
|
+
expect(conv.participants.find((p) => p.agent_id === "bob")!.role).toBe("reviewer");
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
it("does not emit mail.participant.joined for a duplicate invite", async () => {
|
|
241
|
+
const listener = vi.fn();
|
|
242
|
+
const createResp = await rpc("mail/create", { subject: "Test" });
|
|
243
|
+
const convId = (createResp.result as { id: string }).id;
|
|
244
|
+
events.on("mail.participant.joined", listener);
|
|
245
|
+
|
|
246
|
+
await rpc("mail/invite", { conversationId: convId, agentId: "bob" });
|
|
247
|
+
await rpc("mail/invite", { conversationId: convId, agentId: "bob" });
|
|
248
|
+
|
|
249
|
+
expect(listener).toHaveBeenCalledOnce();
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
it("errors on a missing conversation", async () => {
|
|
253
|
+
const resp = await rpc("mail/invite", { conversationId: "nope", agentId: "bob" });
|
|
254
|
+
expect(resp.error?.code).toBe(-32001);
|
|
255
|
+
});
|
|
148
256
|
});
|
|
149
257
|
|
|
150
258
|
describe("mail/turn", () => {
|
package/AGENTS.md
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
# Agent Instructions
|
|
2
|
-
|
|
3
|
-
<!-- SWARMKIT-WIKI:START -->
|
|
4
|
-
## SwarmKit Ecosystem Knowledge Base
|
|
5
|
-
|
|
6
|
-
This repository participates in the SwarmKit ecosystem. Before changing architecture, package boundaries, cross-repo integrations, protocols, task/dispatch behavior, memory/learning flows, workspace/git behavior, or agent orchestration semantics, query the shared knowledge base:
|
|
7
|
-
|
|
8
|
-
```sh
|
|
9
|
-
node /Users/alexngai/GitHub/swarmkit-wiki/scripts/query-knowledge.mjs context --cwd "$PWD"
|
|
10
|
-
node /Users/alexngai/GitHub/swarmkit-wiki/scripts/query-knowledge.mjs repo agent-inbox
|
|
11
|
-
node /Users/alexngai/GitHub/swarmkit-wiki/scripts/query-knowledge.mjs interactions agent-inbox
|
|
12
|
-
node /Users/alexngai/GitHub/swarmkit-wiki/scripts/query-knowledge.mjs search "<concept>"
|
|
13
|
-
```
|
|
14
|
-
|
|
15
|
-
Canonical ecosystem memory lives at `/Users/alexngai/GitHub/swarmkit-wiki`.
|
|
16
|
-
|
|
17
|
-
When this repo changes knowledge that should persist across agents, update the relevant wiki article, semantic model, raw snapshot, graph artifact, or cross-repo interaction data in `swarmkit-wiki`. Do not treat this repo's local `.understand-anything/` cache as canonical; graph artifacts are centralized in `swarmkit-wiki/.understand-anything/graphs/`.
|
|
18
|
-
<!-- SWARMKIT-WIKI:END -->
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Durable persistence for the delivery queue.
|
|
3
|
-
*
|
|
4
|
-
* The DeliveryQueue keeps an in-memory index for fast reads and write-through
|
|
5
|
-
* journals every mutation to a QueueStore. On construction it hydrates the
|
|
6
|
-
* in-memory index from the store, so queued messages survive a restart.
|
|
7
|
-
*
|
|
8
|
-
* Two impls: the default in-memory behavior needs no store (pass none), and
|
|
9
|
-
* SqliteQueueStore backs durable mode (DeliveryQueueConfig.persistence = "sqlite").
|
|
10
|
-
*/
|
|
11
|
-
import type Database from "better-sqlite3";
|
|
12
|
-
import type { QueuedMessage } from "../types.js";
|
|
13
|
-
export interface QueueStore {
|
|
14
|
-
/** Insert or replace an entry. */
|
|
15
|
-
upsert(entry: QueuedMessage): void;
|
|
16
|
-
/** Remove a single entry by peer + message id. */
|
|
17
|
-
remove(peerId: string, messageId: string): void;
|
|
18
|
-
/** Remove all entries for a peer (e.g. on flush). */
|
|
19
|
-
removePeer(peerId: string): void;
|
|
20
|
-
/** Load all persisted entries (called once on hydrate). */
|
|
21
|
-
loadAll(): QueuedMessage[];
|
|
22
|
-
}
|
|
23
|
-
/** SQLite-backed durable queue store using a borrowed better-sqlite3 handle. */
|
|
24
|
-
export declare class SqliteQueueStore implements QueueStore {
|
|
25
|
-
private db;
|
|
26
|
-
private table;
|
|
27
|
-
private upsertStmt;
|
|
28
|
-
private removeStmt;
|
|
29
|
-
private removePeerStmt;
|
|
30
|
-
private loadStmt;
|
|
31
|
-
/**
|
|
32
|
-
* @param db A better-sqlite3 handle (lifecycle owned by the caller).
|
|
33
|
-
* @param prefix Table name prefix to match the Storage convention. Default "".
|
|
34
|
-
*/
|
|
35
|
-
constructor(db: Database.Database, prefix?: string);
|
|
36
|
-
private migrate;
|
|
37
|
-
upsert(entry: QueuedMessage): void;
|
|
38
|
-
remove(peerId: string, messageId: string): void;
|
|
39
|
-
removePeer(peerId: string): void;
|
|
40
|
-
loadAll(): QueuedMessage[];
|
|
41
|
-
}
|
|
42
|
-
//# sourceMappingURL=queue-store.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"queue-store.d.ts","sourceRoot":"","sources":["../../src/federation/queue-store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,QAAQ,MAAM,gBAAgB,CAAC;AAC3C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAEjD,MAAM,WAAW,UAAU;IACzB,kCAAkC;IAClC,MAAM,CAAC,KAAK,EAAE,aAAa,GAAG,IAAI,CAAC;IACnC,kDAAkD;IAClD,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IAChD,qDAAqD;IACrD,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,2DAA2D;IAC3D,OAAO,IAAI,aAAa,EAAE,CAAC;CAC5B;AAED,gFAAgF;AAChF,qBAAa,gBAAiB,YAAW,UAAU;IAY/C,OAAO,CAAC,EAAE;IAXZ,OAAO,CAAC,KAAK,CAAS;IACtB,OAAO,CAAC,UAAU,CAAqB;IACvC,OAAO,CAAC,UAAU,CAAqB;IACvC,OAAO,CAAC,cAAc,CAAqB;IAC3C,OAAO,CAAC,QAAQ,CAAqB;IAErC;;;OAGG;gBAEO,EAAE,EAAE,QAAQ,CAAC,QAAQ,EAC7B,MAAM,GAAE,MAAW;IA4BrB,OAAO,CAAC,OAAO;IAgBf,MAAM,CAAC,KAAK,EAAE,aAAa,GAAG,IAAI;IAYlC,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI;IAI/C,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAIhC,OAAO,IAAI,aAAa,EAAE;CAoB3B"}
|
|
@@ -1,87 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Durable persistence for the delivery queue.
|
|
3
|
-
*
|
|
4
|
-
* The DeliveryQueue keeps an in-memory index for fast reads and write-through
|
|
5
|
-
* journals every mutation to a QueueStore. On construction it hydrates the
|
|
6
|
-
* in-memory index from the store, so queued messages survive a restart.
|
|
7
|
-
*
|
|
8
|
-
* Two impls: the default in-memory behavior needs no store (pass none), and
|
|
9
|
-
* SqliteQueueStore backs durable mode (DeliveryQueueConfig.persistence = "sqlite").
|
|
10
|
-
*/
|
|
11
|
-
/** SQLite-backed durable queue store using a borrowed better-sqlite3 handle. */
|
|
12
|
-
export class SqliteQueueStore {
|
|
13
|
-
db;
|
|
14
|
-
table;
|
|
15
|
-
upsertStmt;
|
|
16
|
-
removeStmt;
|
|
17
|
-
removePeerStmt;
|
|
18
|
-
loadStmt;
|
|
19
|
-
/**
|
|
20
|
-
* @param db A better-sqlite3 handle (lifecycle owned by the caller).
|
|
21
|
-
* @param prefix Table name prefix to match the Storage convention. Default "".
|
|
22
|
-
*/
|
|
23
|
-
constructor(db, prefix = "") {
|
|
24
|
-
this.db = db;
|
|
25
|
-
this.table = `${prefix}delivery_queue`;
|
|
26
|
-
this.migrate();
|
|
27
|
-
this.upsertStmt = this.db.prepare(`INSERT INTO ${this.table}
|
|
28
|
-
(id, peer_id, message, enqueued_at, attempts, last_attempt, next_retry)
|
|
29
|
-
VALUES (@id, @peer_id, @message, @enqueued_at, @attempts, @last_attempt, @next_retry)
|
|
30
|
-
ON CONFLICT(id) DO UPDATE SET
|
|
31
|
-
peer_id = excluded.peer_id,
|
|
32
|
-
message = excluded.message,
|
|
33
|
-
enqueued_at = excluded.enqueued_at,
|
|
34
|
-
attempts = excluded.attempts,
|
|
35
|
-
last_attempt = excluded.last_attempt,
|
|
36
|
-
next_retry = excluded.next_retry`);
|
|
37
|
-
this.removeStmt = this.db.prepare(`DELETE FROM ${this.table} WHERE peer_id = ? AND id = ?`);
|
|
38
|
-
this.removePeerStmt = this.db.prepare(`DELETE FROM ${this.table} WHERE peer_id = ?`);
|
|
39
|
-
this.loadStmt = this.db.prepare(`SELECT id, peer_id, message, enqueued_at, attempts, last_attempt, next_retry
|
|
40
|
-
FROM ${this.table} ORDER BY enqueued_at ASC`);
|
|
41
|
-
}
|
|
42
|
-
migrate() {
|
|
43
|
-
this.db.exec(`
|
|
44
|
-
CREATE TABLE IF NOT EXISTS ${this.table} (
|
|
45
|
-
id TEXT PRIMARY KEY,
|
|
46
|
-
peer_id TEXT NOT NULL,
|
|
47
|
-
message TEXT NOT NULL,
|
|
48
|
-
enqueued_at TEXT NOT NULL,
|
|
49
|
-
attempts INTEGER NOT NULL DEFAULT 0,
|
|
50
|
-
last_attempt TEXT,
|
|
51
|
-
next_retry TEXT
|
|
52
|
-
);
|
|
53
|
-
CREATE INDEX IF NOT EXISTS ${this.table}_peer_idx
|
|
54
|
-
ON ${this.table}(peer_id);
|
|
55
|
-
`);
|
|
56
|
-
}
|
|
57
|
-
upsert(entry) {
|
|
58
|
-
this.upsertStmt.run({
|
|
59
|
-
id: entry.id,
|
|
60
|
-
peer_id: entry.peerId,
|
|
61
|
-
message: JSON.stringify(entry.message),
|
|
62
|
-
enqueued_at: entry.enqueuedAt,
|
|
63
|
-
attempts: entry.attempts,
|
|
64
|
-
last_attempt: entry.lastAttempt ?? null,
|
|
65
|
-
next_retry: entry.nextRetry ?? null,
|
|
66
|
-
});
|
|
67
|
-
}
|
|
68
|
-
remove(peerId, messageId) {
|
|
69
|
-
this.removeStmt.run(peerId, messageId);
|
|
70
|
-
}
|
|
71
|
-
removePeer(peerId) {
|
|
72
|
-
this.removePeerStmt.run(peerId);
|
|
73
|
-
}
|
|
74
|
-
loadAll() {
|
|
75
|
-
const rows = this.loadStmt.all();
|
|
76
|
-
return rows.map((r) => ({
|
|
77
|
-
id: r.id,
|
|
78
|
-
peerId: r.peer_id,
|
|
79
|
-
message: JSON.parse(r.message),
|
|
80
|
-
enqueuedAt: r.enqueued_at,
|
|
81
|
-
attempts: r.attempts,
|
|
82
|
-
lastAttempt: r.last_attempt ?? undefined,
|
|
83
|
-
nextRetry: r.next_retry ?? undefined,
|
|
84
|
-
}));
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
//# sourceMappingURL=queue-store.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"queue-store.js","sourceRoot":"","sources":["../../src/federation/queue-store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAgBH,gFAAgF;AAChF,MAAM,OAAO,gBAAgB;IAYjB;IAXF,KAAK,CAAS;IACd,UAAU,CAAqB;IAC/B,UAAU,CAAqB;IAC/B,cAAc,CAAqB;IACnC,QAAQ,CAAqB;IAErC;;;OAGG;IACH,YACU,EAAqB,EAC7B,SAAiB,EAAE;QADX,OAAE,GAAF,EAAE,CAAmB;QAG7B,IAAI,CAAC,KAAK,GAAG,GAAG,MAAM,gBAAgB,CAAC;QACvC,IAAI,CAAC,OAAO,EAAE,CAAC;QACf,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,EAAE,CAAC,OAAO,CAC/B,eAAe,IAAI,CAAC,KAAK;;;;;;;;;0CASW,CACrC,CAAC;QACF,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,EAAE,CAAC,OAAO,CAC/B,eAAe,IAAI,CAAC,KAAK,+BAA+B,CACzD,CAAC;QACF,IAAI,CAAC,cAAc,GAAG,IAAI,CAAC,EAAE,CAAC,OAAO,CACnC,eAAe,IAAI,CAAC,KAAK,oBAAoB,CAC9C,CAAC;QACF,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,EAAE,CAAC,OAAO,CAC7B;gBACU,IAAI,CAAC,KAAK,2BAA2B,CAChD,CAAC;IACJ,CAAC;IAEO,OAAO;QACb,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC;mCACkB,IAAI,CAAC,KAAK;;;;;;;;;mCASV,IAAI,CAAC,KAAK;aAChC,IAAI,CAAC,KAAK;KAClB,CAAC,CAAC;IACL,CAAC;IAED,MAAM,CAAC,KAAoB;QACzB,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;YAClB,EAAE,EAAE,KAAK,CAAC,EAAE;YACZ,OAAO,EAAE,KAAK,CAAC,MAAM;YACrB,OAAO,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,OAAO,CAAC;YACtC,WAAW,EAAE,KAAK,CAAC,UAAU;YAC7B,QAAQ,EAAE,KAAK,CAAC,QAAQ;YACxB,YAAY,EAAE,KAAK,CAAC,WAAW,IAAI,IAAI;YACvC,UAAU,EAAE,KAAK,CAAC,SAAS,IAAI,IAAI;SACpC,CAAC,CAAC;IACL,CAAC;IAED,MAAM,CAAC,MAAc,EAAE,SAAiB;QACtC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;IACzC,CAAC;IAED,UAAU,CAAC,MAAc;QACvB,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IAClC,CAAC;IAED,OAAO;QACL,MAAM,IAAI,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,EAQ5B,CAAC;QACH,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YACtB,EAAE,EAAE,CAAC,CAAC,EAAE;YACR,MAAM,EAAE,CAAC,CAAC,OAAO;YACjB,OAAO,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC;YAC9B,UAAU,EAAE,CAAC,CAAC,WAAW;YACzB,QAAQ,EAAE,CAAC,CAAC,QAAQ;YACpB,WAAW,EAAE,CAAC,CAAC,YAAY,IAAI,SAAS;YACxC,SAAS,EAAE,CAAC,CAAC,UAAU,IAAI,SAAS;SACrC,CAAC,CAAC,CAAC;IACN,CAAC;CACF"}
|
package/dist/index.d.mts
DELETED
package/dist/index.mjs
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
//# sourceMappingURL=index.mjs.map
|
package/dist/index.mjs.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|