@gorlitzer-labs/apiary 1.13.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.
Files changed (45) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +328 -0
  3. package/dist/agent/index.d.ts +689 -0
  4. package/dist/agent/index.js +34 -0
  5. package/dist/agent/index.js.map +1 -0
  6. package/dist/chunk-5V7QLCGB.js +184 -0
  7. package/dist/chunk-5V7QLCGB.js.map +1 -0
  8. package/dist/chunk-AYQJIAR5.js +152 -0
  9. package/dist/chunk-AYQJIAR5.js.map +1 -0
  10. package/dist/chunk-BZ4VDWC2.js +76 -0
  11. package/dist/chunk-BZ4VDWC2.js.map +1 -0
  12. package/dist/chunk-CFY6Z6Q6.js +95 -0
  13. package/dist/chunk-CFY6Z6Q6.js.map +1 -0
  14. package/dist/chunk-CZHO4DBN.js +1719 -0
  15. package/dist/chunk-CZHO4DBN.js.map +1 -0
  16. package/dist/chunk-D7DXZQWT.js +208 -0
  17. package/dist/chunk-D7DXZQWT.js.map +1 -0
  18. package/dist/chunk-ED6ROH6B.js +2021 -0
  19. package/dist/chunk-ED6ROH6B.js.map +1 -0
  20. package/dist/chunk-PEB6HEFT.js +606 -0
  21. package/dist/chunk-PEB6HEFT.js.map +1 -0
  22. package/dist/chunk-QL66XSDB.js +157 -0
  23. package/dist/chunk-QL66XSDB.js.map +1 -0
  24. package/dist/chunk-TPNFOQA7.js +288 -0
  25. package/dist/chunk-TPNFOQA7.js.map +1 -0
  26. package/dist/chunk-ZEFRNAX6.js +1771 -0
  27. package/dist/chunk-ZEFRNAX6.js.map +1 -0
  28. package/dist/cli/index.d.ts +1 -0
  29. package/dist/cli/index.js +2432 -0
  30. package/dist/cli/index.js.map +1 -0
  31. package/dist/index-Dp3QSGog.d.ts +837 -0
  32. package/dist/index.d.ts +2 -0
  33. package/dist/index.js +22 -0
  34. package/dist/index.js.map +1 -0
  35. package/dist/invites-OFXIQLLN.js +23 -0
  36. package/dist/invites-OFXIQLLN.js.map +1 -0
  37. package/dist/join-CQXEF65B.js +15 -0
  38. package/dist/join-CQXEF65B.js.map +1 -0
  39. package/dist/room-Z7KDRVYD.js +1099 -0
  40. package/dist/room-Z7KDRVYD.js.map +1 -0
  41. package/dist/run-DOADB4T7.js +90 -0
  42. package/dist/run-DOADB4T7.js.map +1 -0
  43. package/dist/update-JGE3WCQB.js +11 -0
  44. package/dist/update-JGE3WCQB.js.map +1 -0
  45. package/package.json +72 -0
@@ -0,0 +1,837 @@
1
+ import { z } from 'zod';
2
+
3
+ /** Core types for apiary — messages, participants, pagination. */
4
+
5
+ /**
6
+ * The four top-level categories events are grouped under.
7
+ *
8
+ * Channels subscribe to one or more categories — they only receive events in
9
+ * their subscription set. Use this to filter what an agent or observer sees:
10
+ *
11
+ * - MESSAGE — chat messages and reactions (sent, edited, deleted, reacted)
12
+ * - PRESENCE — participants joining and leaving
13
+ * - ACTIVITY — agent activity (thinking, tool use, mode changes, compaction)
14
+ * - MENTION — direct @mentions delivered only to the mentioned participant
15
+ */
16
+ declare const EventCategory: {
17
+ readonly MESSAGE: "MESSAGE";
18
+ readonly PRESENCE: "PRESENCE";
19
+ readonly ACTIVITY: "ACTIVITY";
20
+ readonly MENTION: "MENTION";
21
+ };
22
+ type EventCategory = (typeof EventCategory)[keyof typeof EventCategory];
23
+ /**
24
+ * A file or image attached to a message. Discriminated union on `type`.
25
+ *
26
+ * - `type: "path"` — local filesystem reference, intended for same-machine rooms.
27
+ * Receivers should gracefully handle the case where the path is not accessible
28
+ * on their machine (e.g. multi-machine rooms with a tunnel).
29
+ *
30
+ * - `type: "upload"` — server-hosted bytes, fetched via `GET /attachment/:id`
31
+ * with a valid session token. URL uses the server's `publicUrl` (tunnel URL
32
+ * when active, otherwise localhost).
33
+ */
34
+ declare const AttachmentSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
35
+ type: z.ZodLiteral<"path">;
36
+ id: z.ZodString;
37
+ name: z.ZodString;
38
+ mime_type: z.ZodString;
39
+ size: z.ZodOptional<z.ZodNumber>;
40
+ path: z.ZodString;
41
+ }, z.core.$strip>, z.ZodObject<{
42
+ type: z.ZodLiteral<"upload">;
43
+ id: z.ZodString;
44
+ name: z.ZodString;
45
+ mime_type: z.ZodString;
46
+ size: z.ZodNumber;
47
+ url: z.ZodString;
48
+ }, z.core.$strip>], "type">;
49
+ type Attachment = z.infer<typeof AttachmentSchema>;
50
+ /**
51
+ * A chat message. Immutable once stored — edits and deletes are separate events.
52
+ *
53
+ * - `id` — UUID, assigned by the room on creation
54
+ * - `room_id` — the room this message belongs to
55
+ * - `sender_id` — participant ID of the author
56
+ * - `sender_name` — display name at the time of sending (denormalized)
57
+ * - `content` — text body (may be empty if the message is image-only)
58
+ * - `reply_to_id` — if set, this message is a reply to that message ID
59
+ * - `recipients` — if non-empty, this is a whisper: only these participant IDs (plus the sender) can read it
60
+ * - `attachments` — zero or more file/image attachments
61
+ * - `image_url` — legacy single-image URL (kept for backward compat)
62
+ * - `image_mime_type` — MIME type of the legacy image
63
+ * - `image_size_bytes` — size of the legacy image in bytes
64
+ * - `timestamp` — creation time (UTC)
65
+ */
66
+ declare const MessageSchema: z.ZodObject<{
67
+ id: z.ZodDefault<z.ZodString>;
68
+ room_id: z.ZodString;
69
+ sender_id: z.ZodString;
70
+ sender_name: z.ZodString;
71
+ content: z.ZodString;
72
+ reply_to_id: z.ZodDefault<z.ZodNullable<z.ZodString>>;
73
+ recipients: z.ZodDefault<z.ZodArray<z.ZodString>>;
74
+ attachments: z.ZodDefault<z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
75
+ type: z.ZodLiteral<"path">;
76
+ id: z.ZodString;
77
+ name: z.ZodString;
78
+ mime_type: z.ZodString;
79
+ size: z.ZodOptional<z.ZodNumber>;
80
+ path: z.ZodString;
81
+ }, z.core.$strip>, z.ZodObject<{
82
+ type: z.ZodLiteral<"upload">;
83
+ id: z.ZodString;
84
+ name: z.ZodString;
85
+ mime_type: z.ZodString;
86
+ size: z.ZodNumber;
87
+ url: z.ZodString;
88
+ }, z.core.$strip>], "type">>>;
89
+ image_url: z.ZodDefault<z.ZodNullable<z.ZodString>>;
90
+ image_mime_type: z.ZodDefault<z.ZodNullable<z.ZodString>>;
91
+ image_size_bytes: z.ZodDefault<z.ZodNullable<z.ZodNumber>>;
92
+ timestamp: z.ZodDefault<z.ZodDate>;
93
+ }, z.core.$strip>;
94
+ type Message = z.infer<typeof MessageSchema>;
95
+ /**
96
+ * Authority level — what a participant is allowed to do.
97
+ *
98
+ * - `admin` — full control: kick, promote, clear, tunnel, generate all links
99
+ * - `product_owner` — can mute/unmute members, set engagement modes, generate member/guest links
100
+ * - `member` — can send messages, set own mode, generate member/guest links
101
+ * - `guest` — read-only: can catch up and search, but can't send or act
102
+ *
103
+ * Set on join via share token. Admins can change authority mid-session via /promote, /demote, /mute, /unmute.
104
+ */
105
+ type AuthorityLevel = "admin" | "product_owner" | "member" | "guest";
106
+ /** Whether a participant is a human or an agent. */
107
+ type ParticipantType = "human" | "agent";
108
+ /**
109
+ * A participant in a room.
110
+ *
111
+ * - `id` — stable unique ID across all rooms and sessions
112
+ * - `name` — display name (mutable — participants can rename)
113
+ * - `status` — current presence status ("online", "offline", etc.)
114
+ * - `type` — "human" or "agent"
115
+ * - `identifier` — optional stable @-mention slug, e.g. "my-agent".
116
+ * Unlike `name`, this never changes on rename.
117
+ * Used for @-mention matching in addition to the display name.
118
+ * Not all participants have one — guests and anonymous users
119
+ * typically don't.
120
+ * - `authority` — optional authority level. Defaults to "member" if unset.
121
+ */
122
+ interface Participant {
123
+ id: string;
124
+ name: string;
125
+ status: string;
126
+ type: ParticipantType;
127
+ identifier?: string;
128
+ authority?: AuthorityLevel;
129
+ }
130
+ /**
131
+ * A page of results with a cursor for fetching the previous page.
132
+ *
133
+ * All paginated queries return results newest-first. Pass `next_cursor` back
134
+ * to the same query to continue paginating backwards through history.
135
+ *
136
+ * - `items` — results for this page (newest-first)
137
+ * - `next_cursor` — pass this to get the next (older) page; null when exhausted
138
+ * - `has_more` — true if there are older items beyond this page
139
+ */
140
+ interface PaginatedResult<T> {
141
+ items: T[];
142
+ next_cursor: string | null;
143
+ has_more: boolean;
144
+ }
145
+
146
+ /**
147
+ * Typed events for rooms — a discriminated union on the `type` field.
148
+ *
149
+ * Every event has:
150
+ * id — UUID assigned at creation
151
+ * type — the discriminant (e.g. "MessageSent", "ParticipantJoined")
152
+ * category — the EventCategory for subscription filtering
153
+ * room_id — the room this event belongs to
154
+ * participant_id — the participant who caused the event
155
+ * timestamp — UTC creation time
156
+ *
157
+ * Use `createEvent()` to build events — it fills in `id` and `timestamp`.
158
+ * Use `EVENT_ROLE` to classify events by their semantic role.
159
+ */
160
+
161
+ interface BaseRoomEvent {
162
+ /** UUID assigned at creation. */
163
+ id: string;
164
+ }
165
+ /** Someone sent a message. `message` contains the full content including any image. */
166
+ interface MessageSentEvent extends BaseRoomEvent {
167
+ type: "MessageSent";
168
+ category: "MESSAGE";
169
+ room_id: string;
170
+ participant_id: string;
171
+ timestamp: Date;
172
+ message: Message;
173
+ }
174
+ /** A message's text content was changed. Does not update image attachments. */
175
+ interface MessageEditedEvent extends BaseRoomEvent {
176
+ type: "MessageEdited";
177
+ category: "MESSAGE";
178
+ room_id: string;
179
+ participant_id: string;
180
+ timestamp: Date;
181
+ message_id: string;
182
+ new_content: string;
183
+ old_content: string;
184
+ }
185
+ /** A message was removed. The message ID is preserved for reference but content is gone. */
186
+ interface MessageDeletedEvent extends BaseRoomEvent {
187
+ type: "MessageDeleted";
188
+ category: "MESSAGE";
189
+ room_id: string;
190
+ participant_id: string;
191
+ timestamp: Date;
192
+ message_id: string;
193
+ }
194
+ /** A participant added an emoji reaction to a message. */
195
+ interface ReactionAddedEvent extends BaseRoomEvent {
196
+ type: "ReactionAdded";
197
+ category: "MESSAGE";
198
+ room_id: string;
199
+ participant_id: string;
200
+ timestamp: Date;
201
+ message_id: string;
202
+ emoji: string;
203
+ }
204
+ /** A participant removed their emoji reaction from a message. */
205
+ interface ReactionRemovedEvent extends BaseRoomEvent {
206
+ type: "ReactionRemoved";
207
+ category: "MESSAGE";
208
+ room_id: string;
209
+ participant_id: string;
210
+ timestamp: Date;
211
+ message_id: string;
212
+ emoji: string;
213
+ }
214
+ /**
215
+ * A participant connected to the room.
216
+ *
217
+ * Not emitted for silent connects (`Room.connect(..., silent: true)`).
218
+ * Agents connect silently to avoid polluting the chat with join noise.
219
+ */
220
+ interface ParticipantJoinedEvent extends BaseRoomEvent {
221
+ type: "ParticipantJoined";
222
+ category: "PRESENCE";
223
+ room_id: string;
224
+ participant_id: string;
225
+ timestamp: Date;
226
+ /** Full participant snapshot at join time. */
227
+ participant: Participant;
228
+ }
229
+ /**
230
+ * A participant disconnected from the room.
231
+ *
232
+ * Not emitted for silent disconnects (`channel.disconnect(true)`).
233
+ */
234
+ interface ParticipantLeftEvent extends BaseRoomEvent {
235
+ type: "ParticipantLeft";
236
+ category: "PRESENCE";
237
+ room_id: string;
238
+ participant_id: string;
239
+ timestamp: Date;
240
+ /** Full participant snapshot at leave time. */
241
+ participant: Participant;
242
+ }
243
+ /**
244
+ * A participant's presence status changed.
245
+ *
246
+ * Emitted by the server for all presence transitions:
247
+ * - `online → unresponsive` — first ping timeout threshold missed (reason: "ping_timeout")
248
+ * - `unresponsive → online` — SSE reconnect recovery (reason: "recovered")
249
+ * - `unresponsive → offline` — sustained timeout, second threshold missed (reason: "ping_timeout")
250
+ * - `online → offline` — clean disconnect (reason: "left") or admin kick (reason: "kicked")
251
+ *
252
+ * The `"left"` and `"kicked"` variants are companion events emitted alongside
253
+ * `ParticipantLeftEvent` / `ParticipantKickedEvent` for state-machine consumers
254
+ * that only want a single event type for presence tracking.
255
+ *
256
+ * `previous_status: "offline"` never appears — re-entry is `ParticipantJoinedEvent`.
257
+ */
258
+ interface StatusChangedEvent extends BaseRoomEvent {
259
+ type: "StatusChanged";
260
+ category: "PRESENCE";
261
+ room_id: string;
262
+ participant_id: string;
263
+ timestamp: Date;
264
+ /** Display name of the affected participant. */
265
+ name: string;
266
+ status: "online" | "unresponsive" | "offline";
267
+ previous_status: "online" | "unresponsive" | "offline";
268
+ reason: "ping_timeout" | "recovered" | "left" | "kicked";
269
+ }
270
+ /**
271
+ * A participant was kicked from the room by an admin.
272
+ *
273
+ * Emitted before the channel disconnect so all participants see it.
274
+ * The subsequent `ParticipantLeft` event is suppressed (silent disconnect).
275
+ */
276
+ interface ParticipantKickedEvent extends BaseRoomEvent {
277
+ type: "ParticipantKicked";
278
+ category: "PRESENCE";
279
+ room_id: string;
280
+ /** The kicked participant. */
281
+ participant_id: string;
282
+ timestamp: Date;
283
+ /** Full participant snapshot at kick time. */
284
+ participant: Participant;
285
+ /** Display name of the admin who kicked them. */
286
+ kicked_by: string;
287
+ }
288
+ /**
289
+ * A participant's authority level was changed by an admin.
290
+ *
291
+ * Covers mute (→ guest), unmute (→ member), and promotion (→ admin).
292
+ */
293
+ interface AuthorityChangedEvent extends BaseRoomEvent {
294
+ type: "AuthorityChanged";
295
+ category: "PRESENCE";
296
+ room_id: string;
297
+ /** The affected participant. */
298
+ participant_id: string;
299
+ timestamp: Date;
300
+ /** Full participant snapshot (with updated authority). */
301
+ participant: Participant;
302
+ /** The new authority level. */
303
+ new_authority: AuthorityLevel;
304
+ /** Display name of the admin who made the change. */
305
+ changed_by: string;
306
+ }
307
+ /**
308
+ * An agent made an MCP tool call.
309
+ *
310
+ * Emitted twice per tool call: once with `status: "started"` (before the call)
311
+ * and once with `status: "completed"` (after). Useful for showing live tool
312
+ * progress in a UI.
313
+ */
314
+ interface ToolUseEvent extends BaseRoomEvent {
315
+ type: "ToolUse";
316
+ category: "ACTIVITY";
317
+ room_id: string;
318
+ participant_id: string;
319
+ timestamp: Date;
320
+ tool_name: string;
321
+ /** "started" before the call, "completed" after. */
322
+ status: "started" | "completed";
323
+ }
324
+ /**
325
+ * A generic activity event for platform-specific actions.
326
+ *
327
+ * Used when no specific event type fits. The `action` field identifies the
328
+ * action (e.g. `"mode_changed"`), and `detail` carries structured metadata.
329
+ *
330
+ * Current usages:
331
+ * - action: "mode_changed", detail: { mode: EngagementMode }
332
+ * Emitted when an agent's engagement mode changes for a room.
333
+ */
334
+ interface ActivityEvent extends BaseRoomEvent {
335
+ type: "Activity";
336
+ category: "ACTIVITY";
337
+ room_id: string;
338
+ participant_id: string;
339
+ timestamp: Date;
340
+ action: string;
341
+ detail: Record<string, unknown> | null;
342
+ }
343
+ /**
344
+ * An agent's context window was compacted.
345
+ *
346
+ * Fired when the LLM backend summarizes and compresses the conversation history
347
+ * to free up context space. After compaction, the agent re-reads all rooms via
348
+ * `catch_up` to rebuild its working context.
349
+ */
350
+ interface ContextCompactedEvent extends BaseRoomEvent {
351
+ type: "ContextCompacted";
352
+ category: "ACTIVITY";
353
+ room_id: string;
354
+ participant_id: string;
355
+ timestamp: Date;
356
+ /** Full participant snapshot (for display name). */
357
+ participant: Participant;
358
+ }
359
+ /**
360
+ * An admin cleared the room history.
361
+ *
362
+ * All clients should discard their local event/message state and start fresh.
363
+ * The event itself is broadcast but NOT persisted (storage is already wiped).
364
+ */
365
+ interface RoomClearedEvent extends BaseRoomEvent {
366
+ type: "RoomCleared";
367
+ category: "ACTIVITY";
368
+ room_id: string;
369
+ participant_id: string;
370
+ timestamp: Date;
371
+ /** Display name of the admin who cleared the room. */
372
+ cleared_by: string;
373
+ }
374
+ /**
375
+ * Room rules were created or updated.
376
+ *
377
+ * Emitted when the rules file changes — either via `PUT /rules` (admin) or
378
+ * when the underlying `~/.apiary/rooms/<name>.rules.md` file is hand-edited
379
+ * (mtime watch). Surfaced by `apiary__catch_up` so long-running agents
380
+ * re-discover the current ruleset after compaction or mid-session edits.
381
+ */
382
+ interface RulesChangedEvent extends BaseRoomEvent {
383
+ type: "RulesChanged";
384
+ category: "ACTIVITY";
385
+ room_id: string;
386
+ participant_id: string;
387
+ timestamp: Date;
388
+ /** The full ruleset in effect after this change. */
389
+ rules: string[];
390
+ /** Display name of the actor who updated the rules; "[system]" if hand-edit. */
391
+ updated_by: string;
392
+ }
393
+ /**
394
+ * A participant was @mentioned in a message.
395
+ *
396
+ * Delivered only to the mentioned participant's channel — not broadcast to the
397
+ * room. The `participant_id` is the **recipient** (the person mentioned), not
398
+ * the sender. The sender is in `message.sender_id`.
399
+ *
400
+ * @mention detection is case-insensitive and matches on both `identifier`
401
+ * (e.g. `@my-agent`) and display `name` (e.g. `@Alice`).
402
+ *
403
+ * Agents in standby modes wake up on this event type only.
404
+ */
405
+ interface MentionedEvent extends BaseRoomEvent {
406
+ type: "Mentioned";
407
+ category: "MENTION";
408
+ room_id: string;
409
+ /** The mentioned participant (the recipient of the @mention). */
410
+ participant_id: string;
411
+ timestamp: Date;
412
+ /** The full message that contained the @mention. */
413
+ message: Message;
414
+ }
415
+ /**
416
+ * A participant was pinged for a status check.
417
+ *
418
+ * Delivered to the pinged participant's channel and all observers.
419
+ * Unlike @mentions, pings are non-blocking: active agents see them as
420
+ * buffered context ("content"), not triggers. Standby agents wake on ping.
421
+ */
422
+ interface PingedEvent extends BaseRoomEvent {
423
+ type: "Pinged";
424
+ category: "MENTION";
425
+ room_id: string;
426
+ /** The pinged participant (the recipient). */
427
+ participant_id: string;
428
+ timestamp: Date;
429
+ /** Participant ID of the person who sent the ping. */
430
+ pinger_id: string;
431
+ /** Display name of the person who sent the ping. */
432
+ pinger_name: string;
433
+ }
434
+ /**
435
+ * A whisper was sent to specific participants.
436
+ *
437
+ * Broadcast to all participants NOT in the whisper, so they know a private
438
+ * conversation is happening without seeing the content. Sender and recipients
439
+ * receive the full `MessageSentEvent` instead. Not persisted to storage.
440
+ */
441
+ interface WhisperNotifiedEvent extends BaseRoomEvent {
442
+ type: "WhisperNotified";
443
+ category: "ACTIVITY";
444
+ room_id: string;
445
+ /** The sender's participant ID (follows the common event convention). */
446
+ participant_id: string;
447
+ timestamp: Date;
448
+ sender_id: string;
449
+ sender_name: string;
450
+ recipient_ids: string[];
451
+ recipient_names: string[];
452
+ }
453
+ type RoomEvent = MessageSentEvent | MessageEditedEvent | MessageDeletedEvent | ReactionAddedEvent | ReactionRemovedEvent | ParticipantJoinedEvent | ParticipantLeftEvent | ParticipantKickedEvent | AuthorityChangedEvent | StatusChangedEvent | ToolUseEvent | ActivityEvent | MentionedEvent | PingedEvent | ContextCompactedEvent | RoomClearedEvent | RulesChangedEvent | WhisperNotifiedEvent;
454
+ /**
455
+ * Semantic role of an event, used by the engagement system to decide how to
456
+ * handle it relative to an agent.
457
+ *
458
+ * - "message" — a direct chat message; may trigger LLM evaluation
459
+ * - "mention" — an @mention directed at a specific participant
460
+ * - "ambient" — background activity (joins, leaves, reactions) — buffered as
461
+ * context but doesn't trigger evaluation on its own
462
+ * - "internal" — platform bookkeeping (edits, deletes, status changes, agent
463
+ * activity) — always dropped by the engagement system
464
+ */
465
+ type EventRole = "message" | "mention" | "ping" | "ambient" | "internal";
466
+ /** Maps each event type to its semantic role for the engagement system. */
467
+ declare const EVENT_ROLE: Record<RoomEvent["type"], EventRole>;
468
+ type EventData<T extends RoomEvent> = Omit<T, "id" | "timestamp"> & {
469
+ id?: string;
470
+ timestamp?: Date;
471
+ };
472
+ /**
473
+ * Create a typed room event, filling in `id` (UUID) and `timestamp` (now).
474
+ *
475
+ * @example
476
+ * const event = createEvent<MessageSentEvent>({
477
+ * type: "MessageSent",
478
+ * category: "MESSAGE",
479
+ * room_id: "room-1",
480
+ * participant_id: "user-1",
481
+ * message: { ... },
482
+ * });
483
+ */
484
+ declare function createEvent<T extends RoomEvent>(data: EventData<T>): T;
485
+
486
+ declare class Channel {
487
+ readonly participantId: string;
488
+ readonly participantName: string;
489
+ subscriptions: Set<EventCategory>;
490
+ private _room;
491
+ private _queue;
492
+ private _waiters;
493
+ private _disconnected;
494
+ constructor(room: Room, participantId: string, participantName: string, subscriptions: Set<EventCategory>);
495
+ get roomId(): string;
496
+ /**
497
+ * Send a chat message from this participant.
498
+ *
499
+ * Persists the message to storage, broadcasts a `MessageSentEvent` to all
500
+ * participants (including the sender), and fires `MentionedEvent` for any
501
+ * `@name` or `@identifier` patterns found in the content.
502
+ *
503
+ * @param content — message text (may be empty if image is provided)
504
+ * @param replyToId — ID of the message being replied to (optional)
505
+ * @param image — optional legacy image attachment
506
+ * @param attachments — optional array of typed attachments
507
+ * @param recipients — if non-empty, this is a whisper: only these participant IDs (plus sender) receive it
508
+ */
509
+ sendMessage(content: string, replyToId?: string | null, image?: {
510
+ url: string;
511
+ mimeType: string;
512
+ sizeBytes: number;
513
+ } | null, attachments?: Attachment[], recipients?: string[]): Promise<Message>;
514
+ /**
515
+ * Ping another participant for a status check.
516
+ *
517
+ * Creates a `PingedEvent` delivered to the target and all observers.
518
+ * Unlike @mentions, pings are non-blocking for active agents.
519
+ */
520
+ ping(targetId: string): Promise<void>;
521
+ /**
522
+ * Emit a non-message activity event to the room.
523
+ *
524
+ * Use this for platform events: tool use indicators, mode changes, compaction
525
+ * notices, etc. The event is persisted and broadcast to all subscribed
526
+ * participants.
527
+ */
528
+ emit(event: RoomEvent): Promise<void>;
529
+ /**
530
+ * Change which event categories this channel receives.
531
+ * Takes effect immediately — buffered events from unsubscribed categories
532
+ * are not retroactively removed.
533
+ */
534
+ updateSubscriptions(categories: Set<EventCategory>): void;
535
+ /**
536
+ * Leave the room.
537
+ *
538
+ * @param silent — if true, suppresses the `ParticipantLeft` broadcast.
539
+ * Agents disconnect silently to avoid chat noise.
540
+ */
541
+ disconnect(silent?: boolean): Promise<void>;
542
+ /** @internal Called by Room to mark this channel as disconnected without removing from room maps. */
543
+ _markDisconnected(): void;
544
+ /** @internal Called by Room to deliver an incoming event. Filters by subscription. */
545
+ _deliver(event: RoomEvent): void;
546
+ /**
547
+ * Receive the next event, waiting up to `timeoutMs`.
548
+ *
549
+ * Returns null if no event arrives within the timeout. Drains buffered events
550
+ * before waiting. Used by `EventMultiplexer` to fan-in events from multiple
551
+ * rooms into a single stream.
552
+ */
553
+ receive(timeoutMs: number): Promise<RoomEvent | null>;
554
+ /**
555
+ * Async iterator — yields events as they arrive.
556
+ *
557
+ * Used by `EventMultiplexer` to fan-in all room channels into a single stream.
558
+ * The iterator completes when the channel is disconnected.
559
+ *
560
+ * @example
561
+ * for await (const event of channel) {
562
+ * console.log(event.type);
563
+ * }
564
+ */
565
+ [Symbol.asyncIterator](): AsyncIterator<RoomEvent>;
566
+ }
567
+
568
+ /**
569
+ * Storage protocol and reference implementations for apiary rooms.
570
+ *
571
+ * # Implementing StorageProtocol
572
+ *
573
+ * Provide your own implementation to persist messages and events to a real
574
+ * database. Pass it to `new Room(roomId, myStorage)`.
575
+ *
576
+ * Pagination contract (applies to all paginated methods):
577
+ * - Results are returned newest-first.
578
+ * - `cursor` is the ID of the last item on the previous page (exclusive).
579
+ * Pass `null` to start from the most recent.
580
+ * - `next_cursor` in the result is the cursor to pass for the next (older) page.
581
+ * - `has_more` is true if there are older items beyond the current page.
582
+ *
583
+ * @example
584
+ * // Minimal Postgres implementation sketch:
585
+ * class PostgresStorage implements StorageProtocol {
586
+ * async addMessage(message) {
587
+ * await db.query("INSERT INTO messages ...", [message]);
588
+ * return message;
589
+ * }
590
+ * async getMessage(room_id, message_id) {
591
+ * return db.query("SELECT * FROM messages WHERE id = $1", [message_id]);
592
+ * }
593
+ * async getMessages(room_id, limit = 30, cursor = null) {
594
+ * // Fetch `limit` messages before `cursor`, newest-first
595
+ * const rows = await db.query("...");
596
+ * return { items: rows, next_cursor: ..., has_more: ... };
597
+ * }
598
+ * async searchMessages(room_id, query, limit = 10, cursor = null) {
599
+ * // Full-text search, newest-first
600
+ * }
601
+ * async addEvent(event) {
602
+ * await db.query("INSERT INTO events ...", [event]);
603
+ * }
604
+ * async getEvents(room_id, category = null, limit = 50, cursor = null) {
605
+ * // Optional category filter, newest-first
606
+ * }
607
+ * }
608
+ */
609
+
610
+ /**
611
+ * Persistence interface for a room's messages and events.
612
+ *
613
+ * Implement this to back rooms with a real database. The reference
614
+ * `InMemoryStorage` is suitable for testing and single-process local use.
615
+ *
616
+ * All methods operate on a single `room_id` — one storage instance is shared
617
+ * across all rooms (the `room_id` partitions the data).
618
+ */
619
+ interface StorageProtocol {
620
+ /**
621
+ * Persist a message and return it (with any server-assigned fields set).
622
+ * Called automatically by `Channel.sendMessage()`.
623
+ */
624
+ addMessage(message: Message): Promise<Message>;
625
+ /**
626
+ * Look up a single message by ID. Returns null if not found.
627
+ * Used by agents when resolving reply context and message refs.
628
+ */
629
+ getMessage(room_id: string, message_id: string): Promise<Message | null>;
630
+ /**
631
+ * Paginate messages for a room, newest-first.
632
+ *
633
+ * `cursor` — the `id` of the last message on the previous page (exclusive).
634
+ * Pass null to start from the most recent message.
635
+ */
636
+ getMessages(room_id: string, limit?: number, cursor?: string | null): Promise<PaginatedResult<Message>>;
637
+ /**
638
+ * Full-text search across message content, newest-first.
639
+ *
640
+ * `query` — keyword or phrase to search for (case-insensitive).
641
+ * `cursor` — pagination cursor (same semantics as `getMessages`).
642
+ */
643
+ searchMessages(room_id: string, query: string, limit?: number, cursor?: string | null): Promise<PaginatedResult<Message>>;
644
+ /**
645
+ * Persist a room event. Called for every event that passes through the room.
646
+ * Events are append-only — never updated or deleted.
647
+ */
648
+ addEvent(event: RoomEvent): Promise<void>;
649
+ /**
650
+ * Paginate events for a room, newest-first.
651
+ *
652
+ * `category` — optional filter (e.g. EventCategory.MESSAGE). Pass null for all.
653
+ * `cursor` — pagination cursor (index-based for events).
654
+ */
655
+ getEvents(room_id: string, category?: EventCategory | null, limit?: number, cursor?: string | null): Promise<PaginatedResult<RoomEvent>>;
656
+ /**
657
+ * Delete all messages and events for a room.
658
+ * Used by the `/clear` command to wipe room history.
659
+ */
660
+ clearRoom(room_id: string): Promise<void>;
661
+ }
662
+ /**
663
+ * Reference in-memory implementation of `StorageProtocol`.
664
+ *
665
+ * Suitable for tests, development, and single-process local use. All data is
666
+ * lost on process restart — not for production.
667
+ *
668
+ * One instance can serve multiple rooms (data is partitioned by `room_id`).
669
+ */
670
+ declare class InMemoryStorage implements StorageProtocol {
671
+ protected _messages: Map<string, {
672
+ id: string;
673
+ room_id: string;
674
+ sender_id: string;
675
+ sender_name: string;
676
+ content: string;
677
+ reply_to_id: string | null;
678
+ recipients: string[];
679
+ attachments: ({
680
+ type: "path";
681
+ id: string;
682
+ name: string;
683
+ mime_type: string;
684
+ path: string;
685
+ size?: number | undefined;
686
+ } | {
687
+ type: "upload";
688
+ id: string;
689
+ name: string;
690
+ mime_type: string;
691
+ size: number;
692
+ url: string;
693
+ })[];
694
+ image_url: string | null;
695
+ image_mime_type: string | null;
696
+ image_size_bytes: number | null;
697
+ timestamp: Date;
698
+ }[]>;
699
+ protected _events: Map<string, RoomEvent[]>;
700
+ addMessage(message: Message): Promise<Message>;
701
+ getMessage(room_id: string, message_id: string): Promise<Message | null>;
702
+ getMessages(room_id: string, limit?: number, cursor?: string | null): Promise<PaginatedResult<Message>>;
703
+ searchMessages(room_id: string, query: string, limit?: number, cursor?: string | null): Promise<PaginatedResult<Message>>;
704
+ addEvent(event: RoomEvent): Promise<void>;
705
+ getEvents(room_id: string, category?: EventCategory | null, limit?: number, cursor?: string | null): Promise<PaginatedResult<RoomEvent>>;
706
+ clearRoom(room_id: string): Promise<void>;
707
+ }
708
+
709
+ /**
710
+ * Room — a shared chat space where humans and agents are all just participants.
711
+ *
712
+ * Transport-agnostic: no WebSockets, no HTTP. The caller owns the transport
713
+ * and passes messages/events in via channels. This means the same Room works
714
+ * identically in a CLI, a web server, or a test.
715
+ *
716
+ * # Connecting
717
+ * Participants connect via `room.connect()`, which returns a `Channel`. The
718
+ * channel is their bidirectional connection: they send messages and receive
719
+ * events through it.
720
+ *
721
+ * # Observing
722
+ * Call `room.observe()` to get a read-only-style channel that receives every
723
+ * event in the room — including targeted @mention events directed at other
724
+ * participants. Observers are NOT participants: they don't appear in
725
+ * `listParticipants()` and don't trigger join/leave events.
726
+ *
727
+ * # @mention detection
728
+ * When a message is sent, the Room scans its content for `@token` patterns and
729
+ * fires a `MentionedEvent` for any participant whose `identifier` or display
730
+ * `name` matches the token (case-insensitive). The mention event is delivered
731
+ * to the mentioned participant AND to all observers.
732
+ *
733
+ * @example
734
+ * const storage = new InMemoryStorage();
735
+ * const room = new Room("room-1", storage);
736
+ *
737
+ * const aliceChannel = await room.connect("alice-id", "Alice");
738
+ * const agentChannel = await room.connect("agent-id", "Agent", "agent", "my-agent");
739
+ * const observer = room.observe();
740
+ *
741
+ * await aliceChannel.sendMessage("hey @my-agent what do you think?");
742
+ * // → MessageSentEvent broadcast to all participants + observer
743
+ * // → MentionedEvent delivered to agentChannel + observer
744
+ */
745
+
746
+ declare class Room {
747
+ readonly roomId: string;
748
+ /** Direct access to the underlying storage. Useful for bulk reads. */
749
+ readonly storage: StorageProtocol;
750
+ private _channels;
751
+ private _participants;
752
+ private _observers;
753
+ private _nextObserverId;
754
+ /**
755
+ * @param roomId — stable identifier for this room (e.g. a UUID or slug)
756
+ * @param storage — storage backend; defaults to `InMemoryStorage`
757
+ */
758
+ constructor(roomId: string, storage?: StorageProtocol);
759
+ /**
760
+ * Connect a participant and return their channel.
761
+ */
762
+ connect(participantId: string, name: string, options?: {
763
+ type?: ParticipantType;
764
+ identifier?: string;
765
+ subscribe?: Set<EventCategory>;
766
+ silent?: boolean;
767
+ authority?: AuthorityLevel;
768
+ }): Promise<Channel>;
769
+ /**
770
+ * Observe all room events without being a participant.
771
+ *
772
+ * Returns a channel that receives every event — broadcasts AND targeted
773
+ * @mention events directed at other participants. Observers do NOT appear
774
+ * in `listParticipants()` and do not emit join/leave presence events,
775
+ * since they are not participants.
776
+ *
777
+ * Disconnect via `observer.disconnect()` when done.
778
+ *
779
+ * @example
780
+ * const observer = room.observe();
781
+ * for await (const event of observer) {
782
+ * // sees everything, including mentions for other participants
783
+ * }
784
+ */
785
+ observe(): Channel;
786
+ /**
787
+ * Paginate messages, newest-first. Pass the returned `next_cursor` to get
788
+ * the next (older) page.
789
+ */
790
+ listMessages(limit?: number, cursor?: string | null): Promise<PaginatedResult<Message>>;
791
+ /**
792
+ * Full-text search across message content, newest-first.
793
+ * `query` is matched case-insensitively against message content.
794
+ */
795
+ searchMessages(query: string, limit?: number, cursor?: string | null): Promise<PaginatedResult<Message>>;
796
+ /** All currently connected participants (including agents). Observers excluded. */
797
+ listParticipants(): Participant[];
798
+ /**
799
+ * Paginate room events, newest-first.
800
+ * `category` optionally filters to one EventCategory.
801
+ */
802
+ listEvents(category?: EventCategory | null, limit?: number, cursor?: string | null): Promise<PaginatedResult<RoomEvent>>;
803
+ /** Look up a single message by ID. Returns null if not found. */
804
+ getMessage(id: string): Promise<Message | null>;
805
+ /** Update a participant's authority level at runtime. */
806
+ setParticipantAuthority(participantId: string, authority: AuthorityLevel): boolean;
807
+ /**
808
+ * @internal
809
+ * Store a message, broadcast MessageSentEvent, and fire MentionedEvents.
810
+ *
811
+ * @mention scanning: looks for `@token` patterns in content and matches
812
+ * against each connected participant's `identifier` and display `name`
813
+ * (case-insensitive). Fires a `MentionedEvent` for each match, delivered
814
+ * to the mentioned participant AND all observers.
815
+ */
816
+ _handleMessage(message: Message): Promise<void>;
817
+ /**
818
+ * @internal
819
+ * Send a ping to a participant. Creates a PingedEvent delivered to the
820
+ * target's channel and all observers (same routing as MentionedEvent).
821
+ */
822
+ _handlePing(senderId: string, senderName: string, targetId: string): Promise<void>;
823
+ /** @internal Store and broadcast an activity event. */
824
+ _handleEvent(event: RoomEvent): Promise<void>;
825
+ /** @internal Remove a channel and optionally broadcast ParticipantLeftEvent. */
826
+ _disconnectChannel(channel: Channel, silent?: boolean): Promise<void>;
827
+ private _storeAndBroadcast;
828
+ private _broadcast;
829
+ /**
830
+ * Scan message content for `@token` patterns and return matching participant IDs.
831
+ * Matches against both `identifier` (e.g. `@my-agent`) and display `name` (e.g. `@Alice`).
832
+ * Case-insensitive. Deduplicates — each participant appears at most once.
833
+ */
834
+ private _detectMentions;
835
+ }
836
+
837
+ export { type Attachment as A, Channel as C, EventCategory as E, InMemoryStorage as I, type Message as M, type Participant as P, type RoomEvent as R, type StatusChangedEvent as S, type ToolUseEvent as T, type PaginatedResult as a, Room as b, type ParticipantType as c, type AuthorityLevel as d, type ActivityEvent as e, type ContextCompactedEvent as f, EVENT_ROLE as g, type EventRole as h, type MentionedEvent as i, type MessageDeletedEvent as j, type MessageEditedEvent as k, MessageSchema as l, type MessageSentEvent as m, type ParticipantJoinedEvent as n, type ParticipantLeftEvent as o, type ReactionAddedEvent as p, type ReactionRemovedEvent as q, type StorageProtocol as r, createEvent as s };