stoops 0.2.0 → 0.2.1
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/dist/agent/index.d.ts +3 -3
- package/dist/agent/index.js +4 -4
- package/dist/{chunk-BLGV3QN4.js → chunk-66EFQ2XO.js} +16 -2
- package/dist/chunk-66EFQ2XO.js.map +1 -0
- package/dist/{chunk-7PKT5MPI.js → chunk-B4LBE5QS.js} +2 -2
- package/dist/{chunk-HQS7HBZR.js → chunk-EPLQQF6S.js} +3 -1
- package/dist/chunk-EPLQQF6S.js.map +1 -0
- package/dist/{chunk-LC5WPWR2.js → chunk-HKFCJO7V.js} +4 -4
- package/dist/{chunk-LC5WPWR2.js.map → chunk-HKFCJO7V.js.map} +1 -1
- package/dist/{chunk-SS5NGUJM.js → chunk-OA3CODNP.js} +3 -3
- package/dist/claude/index.d.ts +2 -2
- package/dist/claude/index.js +3 -3
- package/dist/cli/index.js +43 -21
- package/dist/cli/index.js.map +1 -1
- package/dist/{index-DlxJ95ki.d.ts → index-ByKHLUOe.d.ts} +38 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +2 -2
- package/dist/langgraph/index.d.ts +2 -2
- package/dist/langgraph/index.js +3 -3
- package/dist/{types-CzHDzfHA.d.ts → types-9iTDVOJG.d.ts} +1 -1
- package/package.json +1 -1
- package/dist/chunk-BLGV3QN4.js.map +0 -1
- package/dist/chunk-HQS7HBZR.js.map +0 -1
- /package/dist/{chunk-7PKT5MPI.js.map → chunk-B4LBE5QS.js.map} +0 -0
- /package/dist/{chunk-SS5NGUJM.js.map → chunk-OA3CODNP.js.map} +0 -0
package/dist/agent/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { a as ContentPart, R as RoomResolver, b as RoomConnection, c as RoomDataSource, T as ToolHandlerOptions } from '../types-
|
|
2
|
-
export { A as AgentIdentity, C as ClaudeSessionOptions, I as ILLMSession, d as LLMQueryStats, e as LLMSessionOptions, L as LangGraphSessionOptions, f as LocalRoomDataSource, P as ProcessorBridge, Q as QueryTurn, S as SessionCallbacks } from '../types-
|
|
3
|
-
import { R as RoomEvent, P as Participant, c as ParticipantType, C as Channel, b as Room, M as Message, a as PaginatedResult, E as EventCategory } from '../index-
|
|
1
|
+
import { a as ContentPart, R as RoomResolver, b as RoomConnection, c as RoomDataSource, T as ToolHandlerOptions } from '../types-9iTDVOJG.js';
|
|
2
|
+
export { A as AgentIdentity, C as ClaudeSessionOptions, I as ILLMSession, d as LLMQueryStats, e as LLMSessionOptions, L as LangGraphSessionOptions, f as LocalRoomDataSource, P as ProcessorBridge, Q as QueryTurn, S as SessionCallbacks } from '../types-9iTDVOJG.js';
|
|
3
|
+
import { R as RoomEvent, P as Participant, c as ParticipantType, C as Channel, b as Room, M as Message, a as PaginatedResult, E as EventCategory } from '../index-ByKHLUOe.js';
|
|
4
4
|
import 'zod';
|
|
5
5
|
|
|
6
6
|
/** Event formatting and mode descriptions for stoops agents. */
|
package/dist/agent/index.js
CHANGED
|
@@ -5,11 +5,11 @@ import {
|
|
|
5
5
|
RefMap,
|
|
6
6
|
RemoteRoomDataSource,
|
|
7
7
|
SseMultiplexer
|
|
8
|
-
} from "../chunk-
|
|
8
|
+
} from "../chunk-OA3CODNP.js";
|
|
9
9
|
import "../chunk-5ADJGMXQ.js";
|
|
10
10
|
import {
|
|
11
11
|
createFullMcpServer
|
|
12
|
-
} from "../chunk-
|
|
12
|
+
} from "../chunk-B4LBE5QS.js";
|
|
13
13
|
import {
|
|
14
14
|
StoopsEngagement,
|
|
15
15
|
classifyEvent,
|
|
@@ -19,8 +19,8 @@ import {
|
|
|
19
19
|
getSystemPreamble,
|
|
20
20
|
messageRef,
|
|
21
21
|
participantLabel
|
|
22
|
-
} from "../chunk-
|
|
23
|
-
import "../chunk-
|
|
22
|
+
} from "../chunk-66EFQ2XO.js";
|
|
23
|
+
import "../chunk-EPLQQF6S.js";
|
|
24
24
|
export {
|
|
25
25
|
EventMultiplexer,
|
|
26
26
|
EventProcessor,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
EVENT_ROLE
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-EPLQQF6S.js";
|
|
4
4
|
|
|
5
5
|
// src/agent/prompts.ts
|
|
6
6
|
var MODE_DESCRIPTIONS = {
|
|
@@ -138,6 +138,20 @@ function formatEvent(event, resolveParticipant, replyContext, roomLabel, reactio
|
|
|
138
138
|
const name = event.participant?.name ?? "someone";
|
|
139
139
|
return [{ type: "text", text: `${ts}${r}- ${name} left` }];
|
|
140
140
|
}
|
|
141
|
+
case "ParticipantKicked": {
|
|
142
|
+
const name = event.participant?.name ?? "someone";
|
|
143
|
+
return [{ type: "text", text: `${ts}${r}${name} was kicked` }];
|
|
144
|
+
}
|
|
145
|
+
case "AuthorityChanged": {
|
|
146
|
+
const name = event.participant?.name ?? "someone";
|
|
147
|
+
if (event.new_authority === "observer") {
|
|
148
|
+
return [{ type: "text", text: `${ts}${r}${name} was muted` }];
|
|
149
|
+
}
|
|
150
|
+
if (event.new_authority === "participant") {
|
|
151
|
+
return [{ type: "text", text: `${ts}${r}${name} was unmuted` }];
|
|
152
|
+
}
|
|
153
|
+
return [{ type: "text", text: `${ts}${r}${name} \u2192 ${event.new_authority}` }];
|
|
154
|
+
}
|
|
141
155
|
case "ContextCompacted":
|
|
142
156
|
return null;
|
|
143
157
|
default:
|
|
@@ -689,4 +703,4 @@ export {
|
|
|
689
703
|
handleSendMessage,
|
|
690
704
|
createRuntimeMcpServer
|
|
691
705
|
};
|
|
692
|
-
//# sourceMappingURL=chunk-
|
|
706
|
+
//# sourceMappingURL=chunk-66EFQ2XO.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/agent/prompts.ts","../src/agent/engagement.ts","../src/agent/mcp/runtime.ts","../src/agent/tool-handlers.ts"],"sourcesContent":["/** Event formatting and mode descriptions for stoops agents. */\n\nimport type { Participant } from \"../core/types.js\";\nimport type { RoomEvent } from \"../core/events.js\";\nimport type { ContentPart } from \"./types.js\";\n\n// ── Mode descriptions ────────────────────────────────────────────────────────\n\n/** One-liner mode descriptions used in join_room responses and set_mode. */\nexport const MODE_DESCRIPTIONS: Record<string, string> = {\n \"everyone\": \"All messages are pushed to you.\",\n \"people\": \"Human messages are pushed to you. Agent messages are delivered as context.\",\n \"agents\": \"Agent messages are pushed to you. Human messages are delivered as context.\",\n \"me\": \"Only your person's messages are pushed to you. Others are delivered as context.\",\n \"standby-everyone\": \"Only @mentions are pushed to you.\",\n \"standby-people\": \"Only human @mentions are pushed to you.\",\n \"standby-agents\": \"Only agent @mentions are pushed to you.\",\n \"standby-me\": \"Only your person's @mentions are pushed to you.\",\n};\n\n// ── System preamble ──────────────────────────────────────────────────────────\n\nconst SYSTEM_PREAMBLE = `You are a participant in group chats. You may be connected to multiple rooms at once — events from all of them flow to you, labeled with the room name.\n\n## How this works\n- Messages appear labeled: \"[Design Room] [human] Alice: hey everyone\"\n- Replies: \"[Design Room] [human] Alice (replying to [human] Bob): good point\"\n- @mentions: \"⚡ [Design Room] [human] Alice mentioned you: @Bob what do you think?\"\n- All your tools require a room name as the first parameter\n- Rooms have a stable \\`identifier\\` (e.g., design-room) that doesn't change even if renamed\n- Message references like #3847 are internal tool labels only. Never include them in messages — participants don't see them.\n\n## Your memory\nYou have no persistent memory between sessions. Each time you start, you're waking up fresh. Your conversations are still there — read them via tools.\n\nIf you lack context for something someone references, say so directly — don't invent explanations.\n\n## Your person\nYou were created by someone — that's your person. You know their participant ID from your identity block below. Their messages carry more weight: they're the one who set you up and knows what they want from you. In group rooms, stay tuned to them even when others are talking.\n\nWhen someone who isn't your person addresses you in a group room, respond if it's useful and natural. But don't lose track of who you're ultimately here for.\n\n## Engagement modes\nEach room has a mode controlling when you evaluate and respond:\n- everyone — all messages trigger evaluation. Respond when you have something genuine to add.\n- people — any human message triggers you. Agent messages are buffered as context.\n- agents — any agent message triggers you. Human messages are buffered as context.\n- me — only your person's messages trigger evaluation. Read everything else quietly.\n- standby-everyone — only @mentions wake you. Stay silent unless directly called, by anyone.\n- standby-people — only human @mentions wake you.\n- standby-agents — only agent @mentions wake you.\n- standby-me — only your person's @mention wakes you.\n\nNon-everyone rooms show the mode in the room label (e.g., \"[Design Room — people]\").`;\n\nexport function getSystemPreamble(identifier?: string, personParticipantId?: string): string {\n const lines: string[] = [];\n if (identifier) lines.push(`Your identifier: @${identifier}`);\n if (personParticipantId) lines.push(`Your person's participant ID: ${personParticipantId}`);\n if (identifier) lines.push(`Recognize other participants by their identifier. Address them by their current display name.`);\n const identityBlock = lines.length > 0 ? `## Your identity\\n${lines.join(\"\\n\")}\\n\\n` : \"\";\n return identityBlock + SYSTEM_PREAMBLE;\n}\n\n// ── Formatting ────────────────────────────────────────────────────────────────\n\n/** Short 4-char ref for a message ID, used in transcripts. */\nexport function messageRef(messageId: string): string {\n return messageId.replace(/-/g, \"\").slice(0, 4);\n}\n\n/** Format a participant as a labeled name: \"[human] Alice\" or \"[agent] Quinn\". */\nexport function participantLabel(p: Participant | null, fallback?: string): string {\n if (!p) return fallback ?? \"someone\";\n return `[${p.type}] ${p.name}`;\n}\n\n/** Resolve participant name, with fallback. */\nfunction resolveName(resolveParticipant: (id: string) => Participant | null, id: string, fallback?: string): string {\n return resolveParticipant(id)?.name ?? fallback ?? \"someone\";\n}\n\n/** Format a Date as UTC HH:MM:SS for display in agent transcripts. */\nexport function formatTimestamp(date: Date): string {\n return date.toISOString().slice(11, 19);\n}\n\n/** Convert ContentPart[] back to a plain string (for trace logs and stats). */\nexport function contentPartsToString(parts: ContentPart[]): string {\n return parts.map(p => p.type === \"text\" ? p.text : ` [image: ${p.url}]`).join(\"\");\n}\n\n/** Count visual character width (grapheme clusters) for padding alignment. */\nfunction visualLength(s: string): number {\n // Use Intl.Segmenter if available (Node 16+), otherwise fall back to spread\n if (typeof Intl !== \"undefined\" && Intl.Segmenter) {\n const segmenter = new Intl.Segmenter(undefined, { granularity: \"grapheme\" });\n let count = 0;\n for (const _ of segmenter.segment(s)) count++;\n return count;\n }\n return [...s].length;\n}\n\n/**\n * Format multiline content with room label continuation.\n * First line is returned as-is. Subsequent lines get [room] prefix aligned.\n */\nfunction formatMultilineContent(content: string, roomLabel: string | undefined, prefix: string): string {\n const lines = content.split(\"\\n\");\n if (lines.length <= 1) return content;\n const continuation = roomLabel ? `[${roomLabel}] ` : \"\";\n // Pad continuation to align under the content start (grapheme-aware)\n const pad = \" \".repeat(visualLength(prefix));\n return lines[0] + \"\\n\" + lines.slice(1).map(l => `${pad}${continuation}${l}`).join(\"\\n\");\n}\n\n/**\n * Format a typed event as ContentPart[] for the LLM session.\n * Returns null for events that shouldn't be sent to the LLM (noise).\n *\n * Compact one-liner format:\n * Messages: [14:23:01] #3847 [lobby] Alice: hey everyone\n * Replies: [14:23:01] #9102 [lobby] Alice (→ #3847 Bob): good point\n * Mentions: [14:23:01] #5521 [lobby] ⚡ Alice: @bot what do you think?\n * Joined: [14:23:01] [lobby] + Alice joined\n * Left: [14:23:15] [lobby] - Bob left\n * Reactions: [14:23:20] [lobby] Alice reacted ❤️ to #3847\n */\nexport function formatEvent(\n event: RoomEvent,\n resolveParticipant: (id: string) => Participant | null,\n replyContext?: { senderName: string; content: string } | null,\n roomLabel?: string,\n reactionTarget?: { senderName: string; content: string; isSelf: boolean } | null,\n assignRef?: (messageId: string) => string,\n): ContentPart[] | null {\n const r = roomLabel ? `[${roomLabel}] ` : \"\";\n const ts = `[${formatTimestamp(\"timestamp\" in event ? new Date(event.timestamp as Date) : new Date())}] `;\n const mkRef = (id: string) => `#${assignRef ? assignRef(id) : messageRef(id)}`;\n\n switch (event.type) {\n case \"MessageSent\": {\n const msg = event.message;\n const name = resolveName(resolveParticipant, msg.sender_id, msg.sender_name);\n const ref = mkRef(msg.id);\n const linePrefix = `${ts}${ref} ${r}`;\n let text: string;\n if (msg.reply_to_id && replyContext) {\n const rRef = assignRef ? mkRef(msg.reply_to_id) : ref;\n text = `${linePrefix}${name} (→ ${rRef} ${replyContext.senderName}): ${formatMultilineContent(msg.content, roomLabel, `${linePrefix}${name} (→ ${rRef} ${replyContext.senderName}): `)}`;\n } else {\n text = `${linePrefix}${name}: ${formatMultilineContent(msg.content, roomLabel, `${linePrefix}${name}: `)}`;\n }\n const parts: ContentPart[] = [{ type: \"text\", text }];\n if (msg.image_url) parts.push({ type: \"image\", url: msg.image_url });\n return parts;\n }\n case \"Mentioned\": {\n const msg = event.message;\n const name = resolveName(resolveParticipant, msg.sender_id, msg.sender_name);\n const ref = mkRef(msg.id);\n const linePrefix = `${ts}${ref} ${r}⚡ `;\n const text = `${linePrefix}${name}: ${formatMultilineContent(msg.content, roomLabel, `${linePrefix}${name}: `)}`;\n const parts: ContentPart[] = [{ type: \"text\", text }];\n if (msg.image_url) parts.push({ type: \"image\", url: msg.image_url });\n return parts;\n }\n case \"ToolUse\":\n return null;\n case \"Activity\":\n return null;\n case \"ReactionAdded\": {\n const name = resolveName(resolveParticipant, event.participant_id);\n const targetRef = reactionTarget ? ` to ${mkRef(event.message_id)}` : \"\";\n return [{ type: \"text\", text: `${ts}${r}${name} reacted ${event.emoji}${targetRef}` }];\n }\n case \"ReactionRemoved\":\n return null;\n case \"ParticipantJoined\": {\n const name = event.participant?.name ?? \"someone\";\n return [{ type: \"text\", text: `${ts}${r}+ ${name} joined` }];\n }\n case \"ParticipantLeft\": {\n const name = event.participant?.name ?? \"someone\";\n return [{ type: \"text\", text: `${ts}${r}- ${name} left` }];\n }\n case \"ParticipantKicked\": {\n const name = event.participant?.name ?? \"someone\";\n return [{ type: \"text\", text: `${ts}${r}${name} was kicked` }];\n }\n case \"AuthorityChanged\": {\n const name = event.participant?.name ?? \"someone\";\n if (event.new_authority === \"observer\") {\n return [{ type: \"text\", text: `${ts}${r}${name} was muted` }];\n }\n if (event.new_authority === \"participant\") {\n return [{ type: \"text\", text: `${ts}${r}${name} was unmuted` }];\n }\n return [{ type: \"text\", text: `${ts}${r}${name} → ${event.new_authority}` }];\n }\n case \"ContextCompacted\":\n return null;\n default:\n return null;\n }\n}\n","/**\n * Engagement — controls which room events trigger LLM evaluation.\n *\n * # Overview\n *\n * Every event that reaches an agent is classified into one of three dispositions:\n * - \"trigger\" — evaluate now (start an LLM call)\n * - \"content\" — buffer as context; deliver to LLM on the next trigger\n * - \"drop\" — ignore entirely (not delivered to LLM)\n *\n * The `EngagementStrategy` interface defines this contract. Implement it to\n * customize when your agent responds. The built-in `StoopsEngagement` provides\n * an 8-mode system; `classifyEvent()` is a standalone convenience function\n * using the same logic.\n *\n * # Built-in modes (StoopsEngagement / classifyEvent)\n *\n * Active modes (agent evaluates on matching messages):\n * - \"everyone\" — all messages trigger evaluation\n * - \"people\" — only messages from human participants trigger evaluation\n * - \"agents\" — only messages from other agents trigger evaluation\n * - \"me\" — only messages from the agent's designated owner (\"person\") trigger\n *\n * Standby modes (agent only wakes on @mentions):\n * - \"standby-everyone\" — any @mention wakes the agent\n * - \"standby-people\" — only @mentions from humans wake the agent\n * - \"standby-agents\" — only @mentions from other agents wake the agent\n * - \"standby-me\" — only an @mention from the agent's owner wakes them\n *\n * # Classification rules (in order)\n *\n * 1. Internal events (bookkeeping: edits, deletes, status changes, agent\n * activity) → always drop\n * 2. Self-sent events → drop (the agent ignores its own activity).\n * Exception: mentions are not self-dropped — a standby agent should wake\n * if it is @mentioned, even if the mention event's participant_id is itself.\n * 3. Standby modes: only @mentions directed at the agent from a matching\n * sender → trigger; everything else → drop.\n * 4. Active modes, @mention → drop (the MessageSent event already carries the\n * @mention text; delivering both would be redundant).\n * 5. Active modes, message from matching sender → trigger\n * 6. Active modes, message from non-matching sender → content (buffered context)\n * 7. Active modes, ambient event (join/leave/reaction) → content\n */\n\nimport { EVENT_ROLE } from \"../core/events.js\";\nimport type { RoomEvent } from \"../core/events.js\";\nimport type { ParticipantType } from \"../core/types.js\";\n\n// ── Types ─────────────────────────────────────────────────────────────────────\n\n/**\n * The outcome of classifying an event for a given agent.\n *\n * - \"trigger\" — run an LLM evaluation now\n * - \"content\" — buffer as context; include with the next evaluation\n * - \"drop\" — discard; never shown to the LLM\n */\nexport type EventDisposition = \"trigger\" | \"content\" | \"drop\";\n\n/**\n * Engagement strategy — decides which events trigger LLM evaluation.\n *\n * Implement this to customize when your agent responds. The runtime calls\n * `classify()` for every incoming event and acts on the returned disposition.\n *\n * @example\n * // Respond to everything:\n * class AlwaysEngage implements EngagementStrategy {\n * classify(event, roomId, selfId) {\n * if (event.participant_id === selfId) return \"drop\";\n * return \"trigger\";\n * }\n * }\n */\nexport interface EngagementStrategy {\n /**\n * Classify a room event for this agent.\n *\n * @param event — the room event to classify\n * @param roomId — the room the event came from\n * @param selfId — the agent's own participant ID (to drop self-events)\n * @param senderType — \"human\" or \"agent\" for the event's sender\n * @param senderId — participant ID of the sender. For `Mentioned` events,\n * pass `event.message.sender_id` (who wrote the mention),\n * not `event.participant_id` (who was mentioned).\n */\n classify(\n event: RoomEvent,\n roomId: string,\n selfId: string,\n senderType: ParticipantType,\n senderId: string,\n ): EventDisposition;\n\n /**\n * Return the current engagement mode for a room.\n *\n * Optional — strategies that don't use named modes may omit this.\n * The runtime falls back to `\"everyone\"` when absent.\n */\n getMode?(roomId: string): EngagementMode;\n\n /**\n * Update the engagement mode for a room.\n *\n * Optional — called by the runtime when a room connects with an initial mode\n * or when the user changes the mode at runtime. Strategies that don't use\n * named modes may omit this; the call will be silently ignored.\n */\n setMode?(roomId: string, mode: EngagementMode): void;\n\n /**\n * Called when a room is disconnected from the runtime.\n *\n * Optional — use this for cleanup, e.g. removing per-room state that is\n * no longer needed. Strategies with no per-room state may omit this.\n */\n onRoomDisconnected?(roomId: string): void;\n}\n\n// ── Built-in modes ────────────────────────────────────────────────────────────\n\nexport type EngagementMode =\n | \"me\" | \"people\" | \"agents\" | \"everyone\"\n | \"standby-me\" | \"standby-people\" | \"standby-agents\" | \"standby-everyone\";\n\nexport const VALID_MODES: ReadonlySet<string> = new Set<EngagementMode>([\n \"me\", \"people\", \"agents\", \"everyone\",\n \"standby-me\", \"standby-people\", \"standby-agents\", \"standby-everyone\",\n]);\n\nexport function isValidMode(mode: string): mode is EngagementMode {\n return VALID_MODES.has(mode);\n}\n\n// ── Core classification logic (shared) ────────────────────────────────────────\n\nfunction senderMatches(\n filter: string,\n senderType: ParticipantType,\n senderId: string,\n personParticipantId?: string,\n): boolean {\n switch (filter) {\n case \"everyone\": return true;\n case \"people\": return senderType === \"human\";\n case \"agents\": return senderType === \"agent\";\n case \"me\": return !!personParticipantId && senderId === personParticipantId;\n default: return false;\n }\n}\n\n/** Internal classification logic shared by classifyEvent() and StoopsEngagement. */\nfunction classify(\n event: RoomEvent,\n mode: EngagementMode,\n selfId: string,\n senderType: ParticipantType,\n senderId: string,\n personParticipantId?: string,\n): EventDisposition {\n // 1. Internal events (agent activity, edits, deletes, status) — always drop.\n const role = EVENT_ROLE[event.type];\n if (role === \"internal\") return \"drop\";\n\n // 2. Self-sent events — drop. Skip this check for mentions: a standby agent\n // should wake when @mentioned, and the mention's participant_id is the\n // recipient (the agent itself), not the sender.\n if (role !== \"mention\" && event.participant_id === selfId) return \"drop\";\n\n const isStandby = mode.startsWith(\"standby-\");\n const filter = isStandby ? mode.slice(8) : mode; // \"standby-people\" → \"people\"\n\n // 3. Standby: only @mentions to self from a matching sender trigger;\n // everything else is dropped entirely.\n if (isStandby) {\n if (\n role === \"mention\" &&\n event.participant_id === selfId &&\n senderMatches(filter, senderType, senderId, personParticipantId)\n ) return \"trigger\";\n return \"drop\";\n }\n\n // 4. Active: @mention → drop. The MessageSent event already carries the\n // @mention text, so delivering a separate Mentioned event would be redundant.\n if (role === \"mention\") return \"drop\";\n\n // 5–6. Active: message → trigger if sender matches filter, content otherwise.\n if (role === \"message\") {\n return senderMatches(filter, senderType, senderId, personParticipantId)\n ? \"trigger\"\n : \"content\";\n }\n\n // 7. Active: ambient event (join, leave, reaction, compaction) → buffer as context.\n if (role === \"ambient\") return \"content\";\n\n return \"drop\";\n}\n\n// ── StoopsEngagement (stateful, per-room modes) ──────────────────────────────\n\n/**\n * StoopsEngagement — the built-in engagement strategy.\n *\n * Implements the 8-mode system: 4 active modes (everyone/people/agents/me)\n * and 4 standby modes that only wake on @mentions. Maintains per-room mode\n * state internally.\n *\n * @example\n * const engagement = new StoopsEngagement(\"people\", personId);\n * engagement.setMode(\"room-1\", \"me\");\n * engagement.classify(event, \"room-1\", selfId, \"human\", senderId);\n */\nexport class StoopsEngagement implements EngagementStrategy {\n private _modes = new Map<string, EngagementMode>();\n private _defaultMode: EngagementMode;\n private _personParticipantId?: string;\n\n constructor(defaultMode: EngagementMode, personParticipantId?: string) {\n this._defaultMode = defaultMode;\n this._personParticipantId = personParticipantId;\n }\n\n /** Get the engagement mode for a room. Falls back to the default mode. */\n getMode(roomId: string): EngagementMode {\n return this._modes.get(roomId) ?? this._defaultMode;\n }\n\n /** Set the engagement mode for a room. */\n setMode(roomId: string, mode: EngagementMode): void {\n this._modes.set(roomId, mode);\n }\n\n /** Called when a room is disconnected. Removes the room's mode so it doesn't linger. */\n onRoomDisconnected(roomId: string): void {\n this._modes.delete(roomId);\n }\n\n classify(\n event: RoomEvent,\n roomId: string,\n selfId: string,\n senderType: ParticipantType,\n senderId: string,\n ): EventDisposition {\n const mode = this._modes.get(roomId) ?? this._defaultMode;\n return classify(event, mode, selfId, senderType, senderId, this._personParticipantId);\n }\n}\n\n// ── Standalone convenience function ──────────────────────────────────────────\n\n/**\n * Classify a room event for an agent with the given engagement mode.\n *\n * Pure function — no state, no side effects, no SDK dependency.\n * Uses the same classification logic as `StoopsEngagement` but takes all\n * parameters explicitly. Useful for one-off classification or testing.\n *\n * @param event — the event to classify\n * @param mode — the agent's current engagement mode for this room\n * @param selfId — the agent's own participant ID (to drop self-events)\n * @param senderType — type of the participant who caused the event\n * @param senderId — participant ID of the sender.\n * For `Mentioned` events, pass `event.message.sender_id`\n * (who wrote the mention), not `event.participant_id`\n * (who was mentioned).\n * @param personParticipantId — the agent's owner's participant ID, used in\n * \"me\" and \"standby-me\" modes\n */\nexport function classifyEvent(\n event: RoomEvent,\n mode: EngagementMode,\n selfId: string,\n senderType: ParticipantType,\n senderId: string,\n personParticipantId?: string,\n): EventDisposition {\n return classify(event, mode, selfId, senderType, senderId, personParticipantId);\n}\n","/**\n * Runtime MCP server — local MCP proxy for the client-side agent runtime.\n *\n * Claude Code / OpenCode connects to this local server. Tool calls are routed\n * to the right stoop server via the RoomResolver (which maps room names to\n * RemoteRoomDataSource instances).\n *\n * Tools:\n * Always present:\n * stoops__catch_up(room?) — with room: room catch-up. Without: list rooms + pending invites.\n * stoops__search_by_text(room, query, count?, cursor?)\n * stoops__search_by_message(room, ref, direction?, count?)\n * stoops__send_message(room, content, reply_to?)\n * stoops__set_mode(room, mode)\n * stoops__join_room(url, alias?)\n * stoops__leave_room(room)\n *\n * With --admin flag:\n * stoops__admin__set_mode_for(room, participant, mode)\n * stoops__admin__kick(room, participant)\n */\n\nimport { createServer } from \"node:http\";\nimport { z } from \"zod\";\nimport type { RoomResolver, ToolHandlerOptions } from \"../types.js\";\nimport {\n handleCatchUp,\n handleSearchByText,\n handleSearchByMessage,\n handleSendMessage,\n textResult,\n} from \"../tool-handlers.js\";\nimport { MODE_DESCRIPTIONS } from \"../prompts.js\";\nimport { isValidMode } from \"../engagement.js\";\nimport { EventEmitterAsyncResource } from \"node:events\"\n\nexport interface JoinRoomResult {\n success: boolean;\n error?: string;\n roomName?: string;\n agentName?: string;\n authority?: string;\n mode?: string;\n personName?: string;\n participants?: Array<{ name: string; authority: string }>;\n recentLines?: string[];\n}\n\nexport interface RuntimeMcpServerOptions {\n resolver: RoomResolver;\n toolOptions: ToolHandlerOptions;\n admin?: boolean;\n /** Called when the agent requests joining a new room mid-session. */\n onJoinRoom?: (url: string, alias?: string) => Promise<JoinRoomResult>;\n /** Called when the agent requests leaving a room. */\n onLeaveRoom?: (room: string) => Promise<{ success: boolean; error?: string }>;\n /** Called when the agent changes its own mode. */\n onSetMode?: (room: string, mode: string) => Promise<{ success: boolean; error?: string }>;\n /** Called for admin set-mode-for. */\n onAdminSetModeFor?: (room: string, participant: string, mode: string) => Promise<{ success: boolean; error?: string }>;\n /** Called for admin kick. */\n onAdminKick?: (room: string, participant: string) => Promise<{ success: boolean; error?: string }>;\n /** Called for admin mute (demote to observer). */\n onAdminMute?: (room: string, participant: string) => Promise<{ success: boolean; error?: string }>;\n /** Called for admin unmute (restore to participant). */\n onAdminUnmute?: (room: string, participant: string) => Promise<{ success: boolean; error?: string }>;\n}\n\nexport interface RuntimeMcpServer {\n url: string;\n stop: () => Promise<void>;\n}\n\n/** Format a rich join_room response from the callback result. */\nfunction formatJoinResponse(result: JoinRoomResult): string {\n const lines: string[] = [];\n\n lines.push(`Joined ${result.roomName} as \"${result.agentName}\" (${result.authority})`);\n lines.push(\"\");\n\n // Mode\n if (result.mode) {\n lines.push(`Mode: ${result.mode}`);\n const desc = MODE_DESCRIPTIONS[result.mode];\n if (desc) lines.push(` ${desc}`);\n lines.push(` Change with set_mode.`);\n lines.push(\"\");\n }\n\n // Person\n if (result.personName) {\n lines.push(`Person: ${result.personName}`);\n lines.push(` Your person's messages always reach you regardless of mode.`);\n lines.push(\"\");\n }\n\n // Participants\n if (result.participants && result.participants.length > 0) {\n lines.push(\"Participants:\");\n for (const p of result.participants) {\n lines.push(` ${p.name} (${p.authority})`);\n }\n lines.push(\"\");\n }\n\n // Recent activity\n if (result.recentLines && result.recentLines.length > 0) {\n lines.push(\"Recent:\");\n for (const line of result.recentLines) {\n lines.push(` ${line}`);\n }\n lines.push(\"\");\n lines.push(`${result.recentLines.length} message${result.recentLines.length === 1 ? \"\" : \"s\"} shown. Use catch_up(\"${result.roomName}\") for more.`);\n }\n\n return lines.join(\"\\n\");\n}\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nfunction registerTools(server: any, opts: RuntimeMcpServerOptions): void {\n const { resolver, toolOptions } = opts;\n\n // ── stoops__catch_up ────────────────────────────────────────────────────\n server.tool(\n \"stoops__catch_up\",\n \"List your rooms and status. Call with no arguments to see connected rooms. With a room name, returns recent activity you haven't seen.\",\n {\n room: z.string().optional().describe(\"Room name. Omit to list all connected rooms.\"),\n },\n { readOnlyHint: true },\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n async ({ room }: { room?: string }) => {\n if (!room) {\n const rooms = resolver.listAll();\n if (rooms.length === 0) {\n return textResult(\"Not connected to any rooms.\");\n }\n const lines = [\"Connected rooms:\", \"\"];\n for (const r of rooms) {\n const idPart = r.identifier ? ` [${r.identifier}]` : \"\";\n lines.push(` ${r.name}${idPart} — ${r.mode} (${r.participantCount} participants)`);\n if (r.lastMessage) lines.push(` Last: ${r.lastMessage}`);\n }\n return textResult(lines.join(\"\\n\"));\n }\n return handleCatchUp(resolver, { room }, toolOptions);\n },\n );\n\n // ── stoops__search_by_text ──────────────────────────────────────────────\n server.tool(\n \"stoops__search_by_text\",\n \"Search chat history by keyword.\",\n {\n room: z.string().describe(\"Room name\"),\n query: z.string().describe(\"Keyword or phrase to search for\"),\n count: z.number().int().min(1).max(10).default(3).optional()\n .describe(\"Number of matches (default 3)\"),\n cursor: z.string().optional().describe(\"Pagination cursor\"),\n },\n { readOnlyHint: true },\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n async (args: any) => handleSearchByText(resolver, args, toolOptions) as any,\n );\n\n // ── stoops__search_by_message ──────────────────────────────────────────\n server.tool(\n \"stoops__search_by_message\",\n \"Show messages around a known message ref.\",\n {\n room: z.string().describe(\"Room name\"),\n ref: z.string().describe(\"Message ref (e.g. #3847)\"),\n direction: z.enum([\"before\", \"after\"]).default(\"before\").optional()\n .describe(\"'before' to scroll back, 'after' to scroll forward\"),\n count: z.number().int().min(1).max(50).default(10).optional()\n .describe(\"Number of messages (default 10)\"),\n },\n { readOnlyHint: true },\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n async (args: any) => handleSearchByMessage(resolver, args, toolOptions) as any,\n );\n\n // ── stoops__send_message ────────────────────────────────────────────────\n server.tool(\n \"stoops__send_message\",\n \"Send a message to a room.\",\n {\n room: z.string().describe(\"Room name\"),\n content: z.string().describe(\"Message content. @name will notify that participant — use sparingly.\"),\n reply_to_id: z.string().optional()\n .describe(\"Message ref to reply to (e.g. #3847).\"),\n },\n { readOnlyHint: false, destructiveHint: false },\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n async (args: any) => handleSendMessage(resolver, args, toolOptions) as any,\n );\n\n // ── stoops__set_mode ────────────────────────────────────────────────────\n server.tool(\n \"stoops__set_mode\",\n \"Change your engagement mode. Controls which messages are pushed to you: everyone — all messages, people — human messages only, agents — agent messages only, me — your person only. Prefix with standby- for @mentions only.\",\n {\n room: z.string().describe(\"Room name\"),\n mode: z.string().describe(\"Engagement mode\"),\n },\n { readOnlyHint: false, destructiveHint: false, idempotentHint: true },\n async ({ room, mode }: { room: string; mode: string }) => {\n if (!opts.onSetMode) return textResult(\"Mode changes not supported.\");\n if (!isValidMode(mode)) {\n return textResult(`Invalid mode \"${mode}\". Valid modes: everyone, people, agents, me, standby-everyone, standby-people, standby-agents, standby-me.`);\n }\n const result = await opts.onSetMode(room, mode);\n return result.success\n ? textResult(`Mode set to ${mode} for [${room}].`)\n : textResult(result.error ?? \"Failed to set mode.\");\n },\n );\n\n // ── stoops__join_room ──────────────────────────────────────────────────\n server.tool(\n \"stoops__join_room\",\n \"Join a room. Returns your identity, participants, mode, and recent activity.\",\n {\n url: z.string().describe(\"Share URL to join\"),\n alias: z.string().optional().describe(\"Local alias for the room (if name collides)\"),\n },\n { readOnlyHint: false, destructiveHint: false },\n async ({ url, alias }: { url: string; alias?: string }) => {\n if (!opts.onJoinRoom) return textResult(\"Joining rooms not supported.\");\n const result = await opts.onJoinRoom(url, alias);\n if (!result.success) return textResult(result.error ?? \"Failed to join room.\");\n\n // Rich response if we have room details\n if (result.roomName && result.agentName) {\n return textResult(formatJoinResponse(result));\n }\n return textResult(`Joined room successfully.`);\n },\n );\n\n // ── stoops__leave_room ─────────────────────────────────────────────────\n server.tool(\n \"stoops__leave_room\",\n \"Leave a room. Events stop flowing from it.\",\n {\n room: z.string().describe(\"Room name to leave\"),\n },\n { readOnlyHint: false, destructiveHint: false, idempotentHint: true },\n async ({ room }: { room: string }) => {\n if (!opts.onLeaveRoom) return textResult(\"Leaving rooms not supported.\");\n const result = await opts.onLeaveRoom(room);\n return result.success\n ? textResult(`Left [${room}].`)\n : textResult(result.error ?? \"Failed to leave room.\");\n },\n );\n\n // ── Admin tools (only with --admin flag) ────────────────────────────────\n if (opts.admin) {\n server.tool(\n \"stoops__admin__set_mode_for\",\n \"Admin: set engagement mode for another participant.\",\n {\n room: z.string().describe(\"Room name\"),\n participant: z.string().describe(\"Participant name\"),\n mode: z.string().describe(\"Engagement mode to set\"),\n },\n { readOnlyHint: false, destructiveHint: false, idempotentHint: true },\n async ({ room, participant, mode }: { room: string; participant: string; mode: string }) => {\n if (!opts.onAdminSetModeFor) return textResult(\"Admin mode changes not supported.\");\n if (!isValidMode(mode)) {\n return textResult(`Invalid mode \"${mode}\". Valid modes: everyone, people, agents, me, standby-everyone, standby-people, standby-agents, standby-me.`);\n }\n const result = await opts.onAdminSetModeFor(room, participant, mode);\n return result.success\n ? textResult(`Set ${participant}'s mode to ${mode} in [${room}].`)\n : textResult(result.error ?? \"Failed to set mode.\");\n },\n );\n\n server.tool(\n \"stoops__admin__kick\",\n \"Admin: kick a participant from a room.\",\n {\n room: z.string().describe(\"Room name\"),\n participant: z.string().describe(\"Participant name to kick\"),\n },\n { readOnlyHint: false, destructiveHint: true },\n async ({ room, participant }: { room: string; participant: string }) => {\n if (!opts.onAdminKick) return textResult(\"Admin kick not supported.\");\n const result = await opts.onAdminKick(room, participant);\n return result.success\n ? textResult(`Kicked ${participant} from [${room}].`)\n : textResult(result.error ?? \"Failed to kick participant.\");\n },\n );\n\n server.tool(\n \"stoops__admin__mute\",\n \"Admin: make a participant read-only (demote to observer).\",\n {\n room: z.string().describe(\"Room name\"),\n participant: z.string().describe(\"Participant name to mute\"),\n },\n { readOnlyHint: false, destructiveHint: false, idempotentHint: true },\n async ({ room, participant }: { room: string; participant: string }) => {\n if (!opts.onAdminMute) return textResult(\"Admin mute not supported.\");\n const result = await opts.onAdminMute(room, participant);\n return result.success\n ? textResult(`Muted ${participant} in [${room}] (observer).`)\n : textResult(result.error ?? \"Failed to mute participant.\");\n },\n );\n\n server.tool(\n \"stoops__admin__unmute\",\n \"Admin: restore a muted participant (promote to participant).\",\n {\n room: z.string().describe(\"Room name\"),\n participant: z.string().describe(\"Participant name to unmute\"),\n },\n { readOnlyHint: false, destructiveHint: false, idempotentHint: true },\n async ({ room, participant }: { room: string; participant: string }) => {\n if (!opts.onAdminUnmute) return textResult(\"Admin unmute not supported.\");\n const result = await opts.onAdminUnmute(room, participant);\n return result.success\n ? textResult(`Unmuted ${participant} in [${room}] (participant).`)\n : textResult(result.error ?? \"Failed to unmute participant.\");\n },\n );\n }\n}\n\n/**\n * Create a runtime MCP server on a random localhost port.\n * Returns the URL for --mcp-config and a stop function.\n */\nexport async function createRuntimeMcpServer(\n opts: RuntimeMcpServerOptions,\n): Promise<RuntimeMcpServer> {\n const { McpServer } = await import(\"@modelcontextprotocol/sdk/server/mcp.js\");\n const { StreamableHTTPServerTransport } = await import(\n \"@modelcontextprotocol/sdk/server/streamableHttp.js\"\n );\n\n const httpServer = createServer(async (req, res) => {\n if (req.url !== \"/mcp\") {\n res.writeHead(404).end();\n return;\n }\n\n // Fresh McpServer per request (McpServer only allows one active transport)\n const reqServer = new McpServer({ name: \"stoops_runtime\", version: \"1.0.0\" });\n registerTools(reqServer, opts);\n\n const transport = new StreamableHTTPServerTransport({\n sessionIdGenerator: undefined,\n });\n\n await reqServer.connect(transport);\n\n let body: unknown;\n if (req.method === \"POST\") {\n const chunks: Buffer[] = [];\n for await (const chunk of req) chunks.push(chunk as Buffer);\n try { body = JSON.parse(Buffer.concat(chunks).toString()); } catch { body = undefined; }\n }\n\n await transport.handleRequest(req, res, body);\n });\n\n const port = await new Promise<number>((resolve, reject) => {\n httpServer.listen(0, \"127.0.0.1\", () => {\n const addr = httpServer.address();\n if (addr && typeof addr === \"object\") resolve(addr.port);\n else reject(new Error(\"Could not determine server port\"));\n });\n httpServer.once(\"error\", reject);\n });\n\n const url = `http://127.0.0.1:${port}/mcp`;\n\n let stopPromise: Promise<void> | null = null;\n const stop = () => {\n if (!stopPromise) {\n stopPromise = new Promise<void>((resolve, reject) =>\n httpServer.close((err) => (err ? reject(err) : resolve())),\n );\n }\n return stopPromise;\n };\n\n return { url, stop };\n}\n","/** Pure tool handler logic */\n\nimport type { Message } from \"../core/types.js\";\nimport type { RoomConnection, RoomResolver, ToolHandlerOptions } from \"./types.js\";\nimport { messageRef, formatTimestamp } from \"./prompts.js\";\n\n/** Tool result shape consumed by both backends. */\nexport interface ToolResult {\n content: Array<{ type: \"text\"; text: string }>;\n}\n\n/** Helper: resolve room name or return an error tool result. */\nexport function resolveOrError(resolver: RoomResolver, roomName: string):\n | { error: true; result: ToolResult }\n | { error: false; conn: RoomConnection } {\n const conn = resolver.resolve(roomName);\n if (!conn) {\n return {\n error: true as const,\n result: { content: [{ type: \"text\" as const, text: `Unknown room \"${roomName}\".` }] },\n };\n }\n return { error: false as const, conn };\n}\n\nexport function textResult(text: string): ToolResult {\n return { content: [{ type: \"text\" as const, text }] };\n}\n\n/** Format a single message as a transcript line (shared by catch_up and search tools). */\nexport async function formatMsgLine(\n msg: Message,\n conn: RoomConnection,\n mkRef: (id: string) => string,\n): Promise<string> {\n const ts = formatTimestamp(new Date(msg.timestamp));\n const ref = mkRef(msg.id);\n const imageNote = msg.image_url ? ` [[img:${msg.image_url}]]` : \"\";\n let line = `[${ts}] ${ref} ${msg.sender_name}: ${msg.content}${imageNote}`;\n if (msg.reply_to_id) {\n const target = await conn.dataSource.getMessage(msg.reply_to_id);\n if (target) {\n const targetRef = mkRef(target.id);\n line = `[${ts}] ${ref} ${msg.sender_name} (→ ${targetRef} ${target.sender_name}): ${msg.content}${imageNote}`;\n }\n }\n return line;\n}\n\n/**\n * Build catch-up event lines for a room — shared by the catch_up MCP tool and\n * the runtime's full_catch_up injection. Returns formatted transcript lines for\n * all unseen events (oldest first), marking them as seen.\n */\nexport async function buildCatchUpLines(\n conn: RoomConnection,\n options: Pick<ToolHandlerOptions, \"isEventSeen\" | \"markEventsSeen\" | \"assignRef\">,\n): Promise<string[]> {\n const result = await conn.dataSource.getEvents(null, 50, null);\n const chronological = [...result.items].reverse();\n\n let startIdx = chronological.length;\n for (let i = 0; i < chronological.length; i++) {\n if (!options.isEventSeen?.(chronological[i].id)) {\n startIdx = i;\n break;\n }\n }\n const unseen = chronological.slice(startIdx);\n\n const lines: string[] = [];\n const seenIds: string[] = [];\n const mkRef = (id: string) => `#${options.assignRef?.(id) ?? messageRef(id)}`;\n\n for (const event of unseen) {\n seenIds.push(event.id);\n const ts = formatTimestamp(new Date(event.timestamp));\n\n if (event.type === \"MessageSent\") {\n lines.push(await formatMsgLine(event.message, conn, (id) => mkRef(id)));\n } else if (event.type === \"ParticipantJoined\") {\n const participant = conn.dataSource.listParticipants().find((p) => p.id === event.participant_id);\n const name = participant?.name ?? event.participant_id;\n lines.push(`[${ts}] + ${name} joined`);\n } else if (event.type === \"ParticipantLeft\") {\n const name = event.participant?.name ?? event.participant_id;\n lines.push(`[${ts}] - ${name} left`);\n } else if (event.type === \"ReactionAdded\") {\n const participant = conn.dataSource.listParticipants().find((p) => p.id === event.participant_id);\n const name = participant?.name ?? event.participant_id;\n const target = await conn.dataSource.getMessage(event.message_id);\n const targetRef = target ? ` to ${mkRef(target.id)}` : \"\";\n lines.push(`[${ts}] ${name} reacted ${event.emoji}${targetRef}`);\n }\n // Other event types (ToolUse, Activity, Mentioned, etc.) are skipped\n }\n\n if (seenIds.length > 0) options.markEventsSeen?.(seenIds);\n return lines;\n}\n\n// ── Tool handler functions ────────────────────────────────────────────────────\n\nexport async function handleCatchUp(\n resolver: RoomResolver,\n args: { room: string },\n options: ToolHandlerOptions,\n): Promise<ToolResult> {\n const r = resolveOrError(resolver, args.room);\n if (r.error) return r.result;\n const lines = await buildCatchUpLines(r.conn, options);\n const out: string[] = [`Catching up on [${args.room}]:`];\n if (lines.length > 0) {\n out.push(\"\", ...lines);\n } else {\n out.push(\"\", \"(nothing new)\");\n }\n return { content: [{ type: \"text\" as const, text: out.join(\"\\n\") }] };\n}\n\nexport async function handleSearchByText(\n resolver: RoomResolver,\n args: { room: string; query: string; count?: number; cursor?: string },\n options: ToolHandlerOptions,\n): Promise<ToolResult> {\n const r = resolveOrError(resolver, args.room);\n if (r.error) return r.result;\n const { conn } = r;\n const count = args.count ?? 3;\n const mkRef = (id: string) => `#${options.assignRef?.(id) ?? messageRef(id)}`;\n\n // Get up to 50 matches to know the total; slice to count for display\n const searchResult = await conn.dataSource.searchMessages(args.query, 50, args.cursor ?? null);\n const totalVisible = searchResult.items.length;\n\n if (totalVisible === 0) {\n return textResult(`No messages found in [${args.room}] matching \"${args.query}\".`);\n }\n\n const toShow = searchResult.items.slice(0, count); // newest-first\n\n // Load recent messages for context (before/after lookup)\n const recentResult = await conn.dataSource.getMessages(100, null);\n const recentChron = [...recentResult.items].reverse(); // chronological\n const msgIdxMap = new Map<string, number>();\n recentChron.forEach((m, i) => msgIdxMap.set(m.id, i));\n\n // Process clusters oldest-first (reverse the newest-first results)\n const clusters: string[][] = [];\n let newerCount: number | null = null;\n\n for (const match of [...toShow].reverse()) {\n const cluster: string[] = [];\n const matchIdx = msgIdxMap.get(match.id);\n\n if (matchIdx !== undefined) {\n // Before context\n if (matchIdx > 0) {\n cluster.push(await formatMsgLine(recentChron[matchIdx - 1], conn, mkRef));\n }\n // Match line\n cluster.push((await formatMsgLine(match, conn, mkRef)) + \" ←\");\n // After context\n if (matchIdx < recentChron.length - 1) {\n cluster.push(await formatMsgLine(recentChron[matchIdx + 1], conn, mkRef));\n }\n // Track newer count based on the newest match (first in toShow = newest)\n if (newerCount === null && match.id === toShow[0].id) {\n newerCount = recentChron.length - matchIdx - 1;\n }\n } else {\n // Match is older than the context window — show match only\n cluster.push((await formatMsgLine(match, conn, mkRef)) + \" ←\");\n }\n\n clusters.push(cluster);\n }\n\n // Build output\n const shownCount = toShow.length;\n const totalNote = searchResult.has_more\n ? `showing ${shownCount} of 50+`\n : `${shownCount} of ${totalVisible}`;\n const out: string[] = [`Search results in [${args.room}] for \"${args.query}\" (${totalNote} matches):`, \"\"];\n\n for (let i = 0; i < clusters.length; i++) {\n for (const line of clusters[i]) {\n out.push(` ${line}`);\n }\n if (i < clusters.length - 1) out.push(\"\");\n }\n\n if (newerCount !== null && newerCount > 0) {\n out.push(\"\", `${newerCount} newer message${newerCount === 1 ? \"\" : \"s\"} in this room.`);\n }\n if (searchResult.has_more && searchResult.next_cursor) {\n out.push(`(cursor: \"${searchResult.next_cursor}\" for next ${count} matches)`);\n }\n\n return textResult(out.join(\"\\n\"));\n}\n\nexport async function handleSearchByMessage(\n resolver: RoomResolver,\n args: { room: string; ref: string; direction?: \"before\" | \"after\"; count?: number },\n options: ToolHandlerOptions,\n): Promise<ToolResult> {\n const r = resolveOrError(resolver, args.room);\n if (r.error) return r.result;\n const { conn } = r;\n const direction = args.direction ?? \"before\";\n const count = args.count ?? 10;\n const mkRef = (id: string) => `#${options.assignRef?.(id) ?? messageRef(id)}`;\n\n // Resolve ref to message ID\n const rawRef = args.ref.startsWith(\"#\") ? args.ref.slice(1) : args.ref;\n const anchorId = options.resolveRef?.(rawRef) ?? rawRef;\n const anchor = await conn.dataSource.getMessage(anchorId);\n if (!anchor) return textResult(`Message ${args.ref} not found.`);\n\n // Load recent messages for \"after\" direction and \"newer count\"\n const recentResult = await conn.dataSource.getMessages(100, null);\n const recentChron = [...recentResult.items].reverse(); // chronological\n const anchorIdx = recentChron.findIndex((m) => m.id === anchor.id);\n\n let displayMessages: Message[];\n let newerCount: number;\n\n if (direction === \"before\") {\n // Get count messages before anchor from storage (works for any age)\n const beforeResult = await conn.dataSource.getMessages(count, anchor.id);\n const beforeMessages = [...beforeResult.items].reverse(); // chronological\n displayMessages = [...beforeMessages, anchor];\n newerCount = anchorIdx >= 0 ? recentChron.length - anchorIdx - 1 : 100; // 100+ if too old\n } else {\n // \"after\" — use recent window\n if (anchorIdx >= 0) {\n const afterMessages = recentChron.slice(anchorIdx + 1, anchorIdx + 1 + count);\n displayMessages = [anchor, ...afterMessages];\n newerCount = recentChron.length - anchorIdx - 1 - afterMessages.length;\n } else {\n // Anchor not in recent 100 — can't scroll forward, too many newer messages\n displayMessages = [anchor];\n newerCount = 100;\n }\n }\n\n const anchorRef = mkRef(anchor.id);\n const lines: string[] = [`Context in [${args.room}] around ${anchorRef}:`, \"\"];\n\n for (const msg of displayMessages) {\n const line = await formatMsgLine(msg, conn, mkRef);\n const isAnchor = msg.id === anchor.id;\n lines.push(` ${line}${isAnchor ? \" ←\" : \"\"}`);\n }\n\n if (newerCount > 0) {\n const countLabel = newerCount >= 100 ? \"100+\" : String(newerCount);\n lines.push(\"\", `${countLabel} newer message${newerCount === 1 ? \"\" : \"s\"} in this room.`);\n }\n\n return textResult(lines.join(\"\\n\"));\n}\n\nexport async function handleSendMessage(\n resolver: RoomResolver,\n args: {\n room: string;\n content: string;\n reply_to_id?: string;\n image_url?: string;\n image_mime_type?: string;\n image_size_bytes?: number;\n },\n options: ToolHandlerOptions,\n): Promise<ToolResult> {\n const r = resolveOrError(resolver, args.room);\n if (r.error) return r.result;\n\n const image = args.image_url\n ? {\n url: args.image_url,\n mimeType: args.image_mime_type ?? \"image/*\",\n sizeBytes: args.image_size_bytes ?? 0,\n }\n : null;\n\n // Resolve ref to full UUID — accepts both \"#3847\" and \"3847\"\n let replyToId = args.reply_to_id;\n if (replyToId) {\n const rawRef = replyToId.startsWith(\"#\") ? replyToId.slice(1) : replyToId;\n replyToId = options.resolveRef?.(rawRef) ?? replyToId;\n }\n const message = await r.conn.dataSource.sendMessage(args.content, replyToId, image);\n\n const ref = options.assignRef?.(message.id) ?? messageRef(message.id);\n return textResult(`Message sent #${ref}.`);\n}\n"],"mappings":"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","names":[]}
|
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
handleSearchByMessage,
|
|
4
4
|
handleSearchByText,
|
|
5
5
|
handleSendMessage
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-66EFQ2XO.js";
|
|
7
7
|
|
|
8
8
|
// src/agent/mcp/full.ts
|
|
9
9
|
import { createServer } from "http";
|
|
@@ -112,4 +112,4 @@ async function createFullMcpServer(resolver, options) {
|
|
|
112
112
|
export {
|
|
113
113
|
createFullMcpServer
|
|
114
114
|
};
|
|
115
|
-
//# sourceMappingURL=chunk-
|
|
115
|
+
//# sourceMappingURL=chunk-B4LBE5QS.js.map
|
|
@@ -5,6 +5,8 @@ var EVENT_ROLE = {
|
|
|
5
5
|
Mentioned: "mention",
|
|
6
6
|
ParticipantJoined: "ambient",
|
|
7
7
|
ParticipantLeft: "ambient",
|
|
8
|
+
ParticipantKicked: "ambient",
|
|
9
|
+
AuthorityChanged: "ambient",
|
|
8
10
|
ReactionAdded: "ambient",
|
|
9
11
|
ContextCompacted: "ambient",
|
|
10
12
|
MessageEdited: "internal",
|
|
@@ -22,4 +24,4 @@ export {
|
|
|
22
24
|
EVENT_ROLE,
|
|
23
25
|
createEvent
|
|
24
26
|
};
|
|
25
|
-
//# sourceMappingURL=chunk-
|
|
27
|
+
//# sourceMappingURL=chunk-EPLQQF6S.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/core/events.ts"],"sourcesContent":["/**\n * Typed events for rooms — a discriminated union on the `type` field.\n *\n * Every event has:\n * id — UUID assigned at creation\n * type — the discriminant (e.g. \"MessageSent\", \"ParticipantJoined\")\n * category — the EventCategory for subscription filtering\n * room_id — the room this event belongs to\n * participant_id — the participant who caused the event\n * timestamp — UTC creation time\n *\n * Use `createEvent()` to build events — it fills in `id` and `timestamp`.\n * Use `EVENT_ROLE` to classify events by their semantic role.\n */\n\nimport { v4 as uuidv4 } from \"uuid\";\nimport type { AuthorityLevel, Message, Participant } from \"./types.js\";\n\n// ── Base ─────────────────────────────────────────────────────────────────────\n\ninterface BaseRoomEvent {\n /** UUID assigned at creation. */\n id: string;\n}\n\n// ── MESSAGE category ─────────────────────────────────────────────────────────\n\n/** Someone sent a message. `message` contains the full content including any image. */\nexport interface MessageSentEvent extends BaseRoomEvent {\n type: \"MessageSent\";\n category: \"MESSAGE\";\n room_id: string;\n participant_id: string;\n timestamp: Date;\n message: Message;\n}\n\n/** A message's text content was changed. Does not update image attachments. */\nexport interface MessageEditedEvent extends BaseRoomEvent {\n type: \"MessageEdited\";\n category: \"MESSAGE\";\n room_id: string;\n participant_id: string;\n timestamp: Date;\n message_id: string;\n new_content: string;\n old_content: string;\n}\n\n/** A message was removed. The message ID is preserved for reference but content is gone. */\nexport interface MessageDeletedEvent extends BaseRoomEvent {\n type: \"MessageDeleted\";\n category: \"MESSAGE\";\n room_id: string;\n participant_id: string;\n timestamp: Date;\n message_id: string;\n}\n\n/** A participant added an emoji reaction to a message. */\nexport interface ReactionAddedEvent extends BaseRoomEvent {\n type: \"ReactionAdded\";\n category: \"MESSAGE\";\n room_id: string;\n participant_id: string;\n timestamp: Date;\n message_id: string;\n emoji: string;\n}\n\n/** A participant removed their emoji reaction from a message. */\nexport interface ReactionRemovedEvent extends BaseRoomEvent {\n type: \"ReactionRemoved\";\n category: \"MESSAGE\";\n room_id: string;\n participant_id: string;\n timestamp: Date;\n message_id: string;\n emoji: string;\n}\n\n// ── PRESENCE category ─────────────────────────────────────────────────────────\n\n/**\n * A participant connected to the room.\n *\n * Not emitted for silent connects (`Room.connect(..., silent: true)`).\n * Agents connect silently to avoid polluting the chat with join noise.\n */\nexport interface ParticipantJoinedEvent extends BaseRoomEvent {\n type: \"ParticipantJoined\";\n category: \"PRESENCE\";\n room_id: string;\n participant_id: string;\n timestamp: Date;\n /** Full participant snapshot at join time. */\n participant: Participant;\n}\n\n/**\n * A participant disconnected from the room.\n *\n * Not emitted for silent disconnects (`channel.disconnect(true)`).\n */\nexport interface ParticipantLeftEvent extends BaseRoomEvent {\n type: \"ParticipantLeft\";\n category: \"PRESENCE\";\n room_id: string;\n participant_id: string;\n timestamp: Date;\n /** Full participant snapshot at leave time. */\n participant: Participant;\n}\n\n/** A participant's presence status changed (e.g. \"online\" → \"away\"). */\nexport interface StatusChangedEvent extends BaseRoomEvent {\n type: \"StatusChanged\";\n category: \"PRESENCE\";\n room_id: string;\n participant_id: string;\n timestamp: Date;\n status: \"online\" | \"offline\" | \"away\";\n}\n\n/**\n * A participant was kicked from the room by an admin.\n *\n * Emitted before the channel disconnect so all participants see it.\n * The subsequent `ParticipantLeft` event is suppressed (silent disconnect).\n */\nexport interface ParticipantKickedEvent extends BaseRoomEvent {\n type: \"ParticipantKicked\";\n category: \"PRESENCE\";\n room_id: string;\n /** The kicked participant. */\n participant_id: string;\n timestamp: Date;\n /** Full participant snapshot at kick time. */\n participant: Participant;\n /** Display name of the admin who kicked them. */\n kicked_by: string;\n}\n\n/**\n * A participant's authority level was changed by an admin.\n *\n * Covers mute (→ observer), unmute (→ participant), and promotion (→ admin).\n */\nexport interface AuthorityChangedEvent extends BaseRoomEvent {\n type: \"AuthorityChanged\";\n category: \"PRESENCE\";\n room_id: string;\n /** The affected participant. */\n participant_id: string;\n timestamp: Date;\n /** Full participant snapshot (with updated authority). */\n participant: Participant;\n /** The new authority level. */\n new_authority: AuthorityLevel;\n /** Display name of the admin who made the change. */\n changed_by: string;\n}\n\n// ── ACTIVITY category ─────────────────────────────────────────────────────────\n\n\n/**\n * An agent made an MCP tool call.\n *\n * Emitted twice per tool call: once with `status: \"started\"` (before the call)\n * and once with `status: \"completed\"` (after). Useful for showing live tool\n * progress in a UI.\n */\nexport interface ToolUseEvent extends BaseRoomEvent {\n type: \"ToolUse\";\n category: \"ACTIVITY\";\n room_id: string;\n participant_id: string;\n timestamp: Date;\n tool_name: string;\n /** \"started\" before the call, \"completed\" after. */\n status: \"started\" | \"completed\";\n}\n\n/**\n * A generic activity event for platform-specific actions.\n *\n * Used when no specific event type fits. The `action` field identifies the\n * action (e.g. `\"mode_changed\"`), and `detail` carries structured metadata.\n *\n * Current usages:\n * - action: \"mode_changed\", detail: { mode: EngagementMode }\n * Emitted when an agent's engagement mode changes for a room.\n */\nexport interface ActivityEvent extends BaseRoomEvent {\n type: \"Activity\";\n category: \"ACTIVITY\";\n room_id: string;\n participant_id: string;\n timestamp: Date;\n action: string;\n detail: Record<string, unknown> | null;\n}\n\n/**\n * An agent's context window was compacted.\n *\n * Fired when the LLM backend summarizes and compresses the conversation history\n * to free up context space. After compaction, the agent re-reads all rooms via\n * `catch_up` to rebuild its working context.\n */\nexport interface ContextCompactedEvent extends BaseRoomEvent {\n type: \"ContextCompacted\";\n category: \"ACTIVITY\";\n room_id: string;\n participant_id: string;\n timestamp: Date;\n /** Full participant snapshot (for display name). */\n participant: Participant;\n}\n\n// ── MENTION category ─────────────────────────────────────────────────────────\n\n/**\n * A participant was @mentioned in a message.\n *\n * Delivered only to the mentioned participant's channel — not broadcast to the\n * room. The `participant_id` is the **recipient** (the person mentioned), not\n * the sender. The sender is in `message.sender_id`.\n *\n * @mention detection is case-insensitive and matches on both `identifier`\n * (e.g. `@my-agent`) and display `name` (e.g. `@Alice`).\n *\n * Agents in standby modes wake up on this event type only.\n */\nexport interface MentionedEvent extends BaseRoomEvent {\n type: \"Mentioned\";\n category: \"MENTION\";\n room_id: string;\n /** The mentioned participant (the recipient of the @mention). */\n participant_id: string;\n timestamp: Date;\n /** The full message that contained the @mention. */\n message: Message;\n}\n\n// ── Union ─────────────────────────────────────────────────────────────────────\n\nexport type RoomEvent =\n | MessageSentEvent\n | MessageEditedEvent\n | MessageDeletedEvent\n | ReactionAddedEvent\n | ReactionRemovedEvent\n | ParticipantJoinedEvent\n | ParticipantLeftEvent\n | ParticipantKickedEvent\n | AuthorityChangedEvent\n | StatusChangedEvent\n | ToolUseEvent\n | ActivityEvent\n | MentionedEvent\n | ContextCompactedEvent;\n\n// ── Event roles ───────────────────────────────────────────────────────────────\n\n/**\n * Semantic role of an event, used by the engagement system to decide how to\n * handle it relative to an agent.\n *\n * - \"message\" — a direct chat message; may trigger LLM evaluation\n * - \"mention\" — an @mention directed at a specific participant\n * - \"ambient\" — background activity (joins, leaves, reactions) — buffered as\n * context but doesn't trigger evaluation on its own\n * - \"internal\" — platform bookkeeping (edits, deletes, status changes, agent\n * activity) — always dropped by the engagement system\n */\nexport type EventRole = \"message\" | \"mention\" | \"ambient\" | \"internal\";\n\n/** Maps each event type to its semantic role for the engagement system. */\nexport const EVENT_ROLE: Record<RoomEvent[\"type\"], EventRole> = {\n MessageSent: \"message\",\n Mentioned: \"mention\",\n ParticipantJoined: \"ambient\",\n ParticipantLeft: \"ambient\",\n ParticipantKicked: \"ambient\",\n AuthorityChanged: \"ambient\",\n ReactionAdded: \"ambient\",\n ContextCompacted: \"ambient\",\n MessageEdited: \"internal\",\n MessageDeleted: \"internal\",\n ReactionRemoved: \"internal\",\n StatusChanged: \"internal\",\n ToolUse: \"internal\",\n Activity: \"internal\",\n};\n\n// ── Factory ───────────────────────────────────────────────────────────────────\n\ntype EventData<T extends RoomEvent> = Omit<T, \"id\" | \"timestamp\"> & {\n id?: string;\n timestamp?: Date;\n};\n\n/**\n * Create a typed room event, filling in `id` (UUID) and `timestamp` (now).\n *\n * @example\n * const event = createEvent<MessageSentEvent>({\n * type: \"MessageSent\",\n * category: \"MESSAGE\",\n * room_id: \"room-1\",\n * participant_id: \"user-1\",\n * message: { ... },\n * });\n */\nexport function createEvent<T extends RoomEvent>(data: EventData<T>): T {\n return { id: uuidv4(), timestamp: new Date(), ...data } as T;\n}\n"],"mappings":";AAeA,SAAS,MAAM,cAAc;AAyQtB,IAAM,aAAmD;AAAA,EAC9D,aAAmB;AAAA,EACnB,WAAmB;AAAA,EACnB,mBAAmB;AAAA,EACnB,iBAAmB;AAAA,EACnB,mBAAmB;AAAA,EACnB,kBAAmB;AAAA,EACnB,eAAmB;AAAA,EACnB,kBAAmB;AAAA,EACnB,eAAmB;AAAA,EACnB,gBAAmB;AAAA,EACnB,iBAAmB;AAAA,EACnB,eAAmB;AAAA,EACnB,SAAmB;AAAA,EACnB,UAAmB;AACrB;AAqBO,SAAS,YAAiC,MAAuB;AACtE,SAAO,EAAE,IAAI,OAAO,GAAG,WAAW,oBAAI,KAAK,GAAG,GAAG,KAAK;AACxD;","names":[]}
|
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
} from "./chunk-5ADJGMXQ.js";
|
|
5
5
|
import {
|
|
6
6
|
createEvent
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-EPLQQF6S.js";
|
|
8
8
|
|
|
9
9
|
// src/core/storage.ts
|
|
10
10
|
function paginate(items, limit, cursor, key) {
|
|
@@ -570,7 +570,7 @@ var PLACES = [
|
|
|
570
570
|
function randomRoomName() {
|
|
571
571
|
const place = PLACES[Math.floor(Math.random() * PLACES.length)];
|
|
572
572
|
const digits = String(Math.floor(Math.random() * 9e3) + 1e3);
|
|
573
|
-
return `${place}-${digits}`;
|
|
573
|
+
return `${place[0].toUpperCase()}${place.slice(1)}-${digits}`;
|
|
574
574
|
}
|
|
575
575
|
var NAMES = [
|
|
576
576
|
"ash",
|
|
@@ -677,7 +677,7 @@ var NAMES = [
|
|
|
677
677
|
function randomName() {
|
|
678
678
|
const name = NAMES[Math.floor(Math.random() * NAMES.length)];
|
|
679
679
|
const digits = String(Math.floor(Math.random() * 9e3) + 1e3);
|
|
680
|
-
return `${name}-${digits}`;
|
|
680
|
+
return `${name[0].toUpperCase()}${name.slice(1)}-${digits}`;
|
|
681
681
|
}
|
|
682
682
|
|
|
683
683
|
export {
|
|
@@ -687,4 +687,4 @@ export {
|
|
|
687
687
|
randomRoomName,
|
|
688
688
|
randomName
|
|
689
689
|
};
|
|
690
|
-
//# sourceMappingURL=chunk-
|
|
690
|
+
//# sourceMappingURL=chunk-HKFCJO7V.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/core/storage.ts","../src/core/channel.ts","../src/core/room.ts","../src/core/names.ts"],"sourcesContent":["/**\n * Storage protocol and reference implementations for stoops rooms.\n *\n * # Implementing StorageProtocol\n *\n * Provide your own implementation to persist messages and events to a real\n * database. Pass it to `new Room(roomId, myStorage)`.\n *\n * Pagination contract (applies to all paginated methods):\n * - Results are returned newest-first.\n * - `cursor` is the ID of the last item on the previous page (exclusive).\n * Pass `null` to start from the most recent.\n * - `next_cursor` in the result is the cursor to pass for the next (older) page.\n * - `has_more` is true if there are older items beyond the current page.\n *\n * @example\n * // Minimal Postgres implementation sketch:\n * class PostgresStorage implements StorageProtocol {\n * async addMessage(message) {\n * await db.query(\"INSERT INTO messages ...\", [message]);\n * return message;\n * }\n * async getMessage(room_id, message_id) {\n * return db.query(\"SELECT * FROM messages WHERE id = $1\", [message_id]);\n * }\n * async getMessages(room_id, limit = 30, cursor = null) {\n * // Fetch `limit` messages before `cursor`, newest-first\n * const rows = await db.query(\"...\");\n * return { items: rows, next_cursor: ..., has_more: ... };\n * }\n * async searchMessages(room_id, query, limit = 10, cursor = null) {\n * // Full-text search, newest-first\n * }\n * async addEvent(event) {\n * await db.query(\"INSERT INTO events ...\", [event]);\n * }\n * async getEvents(room_id, category = null, limit = 50, cursor = null) {\n * // Optional category filter, newest-first\n * }\n * }\n */\n\nimport type { RoomEvent } from \"./events.js\";\nimport type { EventCategory, Message, PaginatedResult } from \"./types.js\";\n\n// ── StorageProtocol ───────────────────────────────────────────────────────────\n\n/**\n * Persistence interface for a room's messages and events.\n *\n * Implement this to back rooms with a real database. The reference\n * `InMemoryStorage` is suitable for testing and single-process local use.\n *\n * All methods operate on a single `room_id` — one storage instance is shared\n * across all rooms (the `room_id` partitions the data).\n */\nexport interface StorageProtocol {\n /**\n * Persist a message and return it (with any server-assigned fields set).\n * Called automatically by `Channel.sendMessage()`.\n */\n addMessage(message: Message): Promise<Message>;\n\n /**\n * Look up a single message by ID. Returns null if not found.\n * Used by agents when resolving reply context and message refs.\n */\n getMessage(room_id: string, message_id: string): Promise<Message | null>;\n\n /**\n * Paginate messages for a room, newest-first.\n *\n * `cursor` — the `id` of the last message on the previous page (exclusive).\n * Pass null to start from the most recent message.\n */\n getMessages(\n room_id: string,\n limit?: number,\n cursor?: string | null,\n ): Promise<PaginatedResult<Message>>;\n\n /**\n * Full-text search across message content, newest-first.\n *\n * `query` — keyword or phrase to search for (case-insensitive).\n * `cursor` — pagination cursor (same semantics as `getMessages`).\n */\n searchMessages(\n room_id: string,\n query: string,\n limit?: number,\n cursor?: string | null,\n ): Promise<PaginatedResult<Message>>;\n\n /**\n * Persist a room event. Called for every event that passes through the room.\n * Events are append-only — never updated or deleted.\n */\n addEvent(event: RoomEvent): Promise<void>;\n\n /**\n * Paginate events for a room, newest-first.\n *\n * `category` — optional filter (e.g. EventCategory.MESSAGE). Pass null for all.\n * `cursor` — pagination cursor (index-based for events).\n */\n getEvents(\n room_id: string,\n category?: EventCategory | null,\n limit?: number,\n cursor?: string | null,\n ): Promise<PaginatedResult<RoomEvent>>;\n}\n\n// ── Pagination helpers (used by InMemoryStorage) ──────────────────────────────\n\n/**\n * Paginate an array by item ID cursor, returning results newest-first.\n * Items are assumed to be stored oldest-first (append order).\n *\n * @internal\n */\nexport function paginate<T>(\n items: T[],\n limit: number,\n cursor: string | null | undefined,\n key: (item: T) => string,\n): PaginatedResult<T> {\n let subset: T[];\n\n if (cursor != null) {\n const cursorIdx = items.findIndex((item) => key(item) === cursor);\n if (cursorIdx === -1) {\n return { items: [], next_cursor: null, has_more: false };\n }\n subset = items.slice(0, cursorIdx);\n } else {\n subset = items;\n }\n\n const page =\n limit < subset.length ? subset.slice(-limit) : subset.slice();\n page.reverse();\n const has_more = subset.length > limit;\n const next_cursor = has_more && page.length > 0 ? key(page[page.length - 1]) : null;\n\n return { items: page, next_cursor, has_more };\n}\n\n/**\n * Paginate an array by positional index cursor, returning results newest-first.\n * Used for events, which don't have stable IDs suitable for ID-based cursors.\n *\n * @internal\n */\nexport function paginateByIndex<T>(\n items: T[],\n limit: number,\n cursor: string | null | undefined,\n): PaginatedResult<T> {\n const parsedCursor = cursor != null ? parseInt(cursor, 10) : items.length;\n const endIdx = Number.isNaN(parsedCursor) ? items.length : parsedCursor;\n const startIdx = Math.max(0, endIdx - limit);\n const page = items.slice(startIdx, endIdx).reverse();\n const has_more = startIdx > 0;\n const next_cursor = has_more ? String(startIdx) : null;\n\n return { items: page, next_cursor, has_more };\n}\n\n// ── InMemoryStorage ───────────────────────────────────────────────────────────\n\n/**\n * Reference in-memory implementation of `StorageProtocol`.\n *\n * Suitable for tests, development, and single-process local use. All data is\n * lost on process restart — not for production.\n *\n * One instance can serve multiple rooms (data is partitioned by `room_id`).\n */\nexport class InMemoryStorage implements StorageProtocol {\n private _messages = new Map<string, Message[]>();\n private _events = new Map<string, RoomEvent[]>();\n\n async addMessage(message: Message): Promise<Message> {\n const list = this._messages.get(message.room_id) ?? [];\n list.push(message);\n this._messages.set(message.room_id, list);\n return message;\n }\n\n async getMessage(room_id: string, message_id: string): Promise<Message | null> {\n const list = this._messages.get(room_id) ?? [];\n return list.find((m) => m.id === message_id) ?? null;\n }\n\n async getMessages(\n room_id: string,\n limit = 30,\n cursor: string | null = null,\n ): Promise<PaginatedResult<Message>> {\n const messages = this._messages.get(room_id) ?? [];\n return paginate(messages, limit, cursor, (m) => m.id);\n }\n\n async searchMessages(\n room_id: string,\n query: string,\n limit = 10,\n cursor: string | null = null,\n ): Promise<PaginatedResult<Message>> {\n const messages = this._messages.get(room_id) ?? [];\n const q = query.toLowerCase();\n const filtered = messages.filter((m) =>\n m.content.toLowerCase().includes(q),\n );\n return paginate(filtered, limit, cursor, (m) => m.id);\n }\n\n async addEvent(event: RoomEvent): Promise<void> {\n const list = this._events.get(event.room_id) ?? [];\n list.push(event);\n this._events.set(event.room_id, list);\n }\n\n async getEvents(\n room_id: string,\n category: EventCategory | null = null,\n limit = 50,\n cursor: string | null = null,\n ): Promise<PaginatedResult<RoomEvent>> {\n let events = this._events.get(room_id) ?? [];\n if (category != null) {\n events = events.filter((e) => e.category === category);\n }\n return paginateByIndex(events, limit, cursor);\n }\n}\n","/**\n * Channel — a participant's bidirectional connection to a room.\n *\n * Created by `Room.connect()`. Never instantiated directly.\n *\n * # Sending\n * - `sendMessage()` — persist and broadcast a chat message\n * - `emit()` — push non-message events (tool use, mode changes, etc.)\n *\n * # Receiving\n * Channels are async-iterable — use `for await (const event of channel)` to\n * consume events. Only events in the channel's `subscriptions` set are\n * delivered. Alternatively, use `receive(timeoutMs)` for polling with a\n * timeout (used by EventMultiplexer).\n *\n * # Lifecycle\n * - `updateSubscriptions()` — change which EventCategories are delivered\n * - `disconnect(silent?)` — leave the room; pass `true` to suppress the\n * ParticipantLeft broadcast\n */\n\nimport type { RoomEvent } from \"./events.js\";\nimport type { EventCategory, Message } from \"./types.js\";\nimport { MessageSchema } from \"./types.js\";\nimport type { Room } from \"./room.js\";\n\ninterface Waiter {\n resolve: (event: RoomEvent) => void;\n reject: (err: Error) => void;\n}\n\nexport class Channel {\n readonly participantId: string;\n readonly participantName: string;\n subscriptions: Set<EventCategory>;\n\n private _room: Room;\n private _queue: RoomEvent[] = [];\n private _waiters: Waiter[] = [];\n private _disconnected = false;\n\n constructor(\n room: Room,\n participantId: string,\n participantName: string,\n subscriptions: Set<EventCategory>,\n ) {\n this._room = room;\n this.participantId = participantId;\n this.participantName = participantName;\n this.subscriptions = subscriptions;\n }\n\n get roomId(): string {\n return this._room.roomId;\n }\n\n /**\n * Send a chat message from this participant.\n *\n * Persists the message to storage, broadcasts a `MessageSentEvent` to all\n * participants (including the sender), and fires `MentionedEvent` for any\n * `@name` or `@identifier` patterns found in the content.\n *\n * @param content — message text (may be empty if image is provided)\n * @param replyToId — ID of the message being replied to (optional)\n * @param image — optional image attachment\n */\n async sendMessage(\n content: string,\n replyToId?: string | null,\n image?: {\n url: string;\n mimeType: string;\n sizeBytes: number;\n } | null,\n ): Promise<Message> {\n if (this._disconnected) {\n throw new Error(\"Channel is disconnected\");\n }\n const message = MessageSchema.parse({\n room_id: this._room.roomId,\n sender_id: this.participantId,\n sender_name: this.participantName,\n content,\n reply_to_id: replyToId ?? null,\n image_url: image?.url ?? null,\n image_mime_type: image?.mimeType ?? null,\n image_size_bytes: image?.sizeBytes ?? null,\n });\n await this._room._handleMessage(message);\n return message;\n }\n\n /**\n * Emit a non-message activity event to the room.\n *\n * Use this for platform events: tool use indicators, mode changes, compaction\n * notices, etc. The event is persisted and broadcast to all subscribed\n * participants.\n */\n async emit(event: RoomEvent): Promise<void> {\n if (this._disconnected) {\n throw new Error(\"Channel is disconnected\");\n }\n await this._room._handleEvent(event);\n }\n\n /**\n * Change which event categories this channel receives.\n * Takes effect immediately — buffered events from unsubscribed categories\n * are not retroactively removed.\n */\n updateSubscriptions(categories: Set<EventCategory>): void {\n this.subscriptions = categories;\n }\n\n /**\n * Leave the room.\n *\n * @param silent — if true, suppresses the `ParticipantLeft` broadcast.\n * Agents disconnect silently to avoid chat noise.\n */\n async disconnect(silent = false): Promise<void> {\n if (!this._disconnected) {\n this._disconnected = true;\n // Wake pending waiters so async iterators exit cleanly\n const waiters = this._waiters;\n this._waiters = [];\n for (const w of waiters) {\n w.reject(new Error(\"Channel disconnected\"));\n }\n await this._room._disconnectChannel(this, silent);\n }\n }\n\n /** @internal Called by Room to mark this channel as disconnected without removing from room maps. */\n _markDisconnected(): void {\n if (!this._disconnected) {\n this._disconnected = true;\n const waiters = this._waiters;\n this._waiters = [];\n for (const w of waiters) {\n w.reject(new Error(\"Channel disconnected\"));\n }\n }\n }\n\n /** @internal Called by Room to deliver an incoming event. Filters by subscription. */\n _deliver(event: RoomEvent): void {\n if (this._disconnected) return;\n if (!this.subscriptions.has(event.category)) return;\n\n if (this._waiters.length > 0) {\n const waiter = this._waiters.shift()!;\n waiter.resolve(event);\n } else {\n this._queue.push(event);\n }\n }\n\n /**\n * Receive the next event, waiting up to `timeoutMs`.\n *\n * Returns null if no event arrives within the timeout. Drains buffered events\n * before waiting. Used by `EventMultiplexer` to fan-in events from multiple\n * rooms into a single stream.\n */\n receive(timeoutMs: number): Promise<RoomEvent | null> {\n if (this._queue.length > 0) {\n return Promise.resolve(this._queue.shift()!);\n }\n if (this._disconnected) {\n return Promise.resolve(null);\n }\n\n return new Promise<RoomEvent | null>((resolve) => {\n let settled = false;\n const waiter: Waiter = {\n resolve: (event) => {\n if (!settled) {\n settled = true;\n clearTimeout(timer);\n resolve(event);\n }\n },\n reject: () => {\n if (!settled) {\n settled = true;\n clearTimeout(timer);\n resolve(null);\n }\n },\n };\n this._waiters.push(waiter);\n\n const timer = setTimeout(() => {\n if (!settled) {\n settled = true;\n const idx = this._waiters.indexOf(waiter);\n if (idx !== -1) this._waiters.splice(idx, 1);\n resolve(null);\n }\n }, timeoutMs);\n });\n }\n\n /**\n * Async iterator — yields events as they arrive.\n *\n * Used by `EventMultiplexer` to fan-in all room channels into a single stream.\n * The iterator completes when the channel is disconnected.\n *\n * @example\n * for await (const event of channel) {\n * console.log(event.type);\n * }\n */\n [Symbol.asyncIterator](): AsyncIterator<RoomEvent> {\n return {\n next: (): Promise<IteratorResult<RoomEvent>> => {\n if (this._queue.length > 0) {\n return Promise.resolve({\n value: this._queue.shift()!,\n done: false,\n });\n }\n\n if (this._disconnected) {\n return Promise.resolve({\n value: undefined as unknown as RoomEvent,\n done: true,\n });\n }\n\n return new Promise<IteratorResult<RoomEvent>>((resolve, reject) => {\n this._waiters.push({\n resolve: (event) => resolve({ value: event, done: false }),\n reject,\n });\n });\n },\n };\n }\n}\n","/**\n * Room — a shared chat space where humans and agents are all just participants.\n *\n * Transport-agnostic: no WebSockets, no HTTP. The caller owns the transport\n * and passes messages/events in via channels. This means the same Room works\n * identically in a CLI, a web server, or a test.\n *\n * # Connecting\n * Participants connect via `room.connect()`, which returns a `Channel`. The\n * channel is their bidirectional connection: they send messages and receive\n * events through it.\n *\n * # Observing\n * Call `room.observe()` to get a read-only-style channel that receives every\n * event in the room — including targeted @mention events directed at other\n * participants. Observers are NOT participants: they don't appear in\n * `listParticipants()` and don't trigger join/leave events.\n *\n * # @mention detection\n * When a message is sent, the Room scans its content for `@token` patterns and\n * fires a `MentionedEvent` for any participant whose `identifier` or display\n * `name` matches the token (case-insensitive). The mention event is delivered\n * to the mentioned participant AND to all observers.\n *\n * @example\n * const storage = new InMemoryStorage();\n * const room = new Room(\"room-1\", storage);\n *\n * const aliceChannel = await room.connect(\"alice-id\", \"Alice\");\n * const agentChannel = await room.connect(\"agent-id\", \"Agent\", \"agent\", \"my-agent\");\n * const observer = room.observe();\n *\n * await aliceChannel.sendMessage(\"hey @my-agent what do you think?\");\n * // → MessageSentEvent broadcast to all participants + observer\n * // → MentionedEvent delivered to agentChannel + observer\n */\n\nimport { Channel } from \"./channel.js\";\nimport { createEvent } from \"./events.js\";\nimport type {\n MentionedEvent,\n MessageSentEvent,\n ParticipantJoinedEvent,\n ParticipantLeftEvent,\n RoomEvent,\n} from \"./events.js\";\nimport { InMemoryStorage, type StorageProtocol } from \"./storage.js\";\nimport { EventCategory, type AuthorityLevel, type Message, type PaginatedResult, type Participant, type ParticipantType } from \"./types.js\";\n\nconst ALL_CATEGORIES = new Set<EventCategory>([\n EventCategory.MESSAGE,\n EventCategory.PRESENCE,\n EventCategory.ACTIVITY,\n EventCategory.MENTION,\n]);\n\nexport class Room {\n readonly roomId: string;\n /** Direct access to the underlying storage. Useful for bulk reads. */\n readonly storage: StorageProtocol;\n private _channels = new Map<string, Channel>();\n private _participants = new Map<string, Participant>();\n private _observers = new Set<Channel>();\n private _nextObserverId = 0;\n\n /**\n * @param roomId — stable identifier for this room (e.g. a UUID or slug)\n * @param storage — storage backend; defaults to `InMemoryStorage`\n */\n constructor(roomId: string, storage?: StorageProtocol) {\n this.roomId = roomId;\n this.storage = storage ?? new InMemoryStorage();\n }\n\n /**\n * Connect a participant and return their channel.\n */\n async connect(\n participantId: string,\n name: string,\n options?: {\n type?: ParticipantType;\n identifier?: string;\n subscribe?: Set<EventCategory>;\n silent?: boolean;\n authority?: AuthorityLevel;\n },\n ): Promise<Channel> {\n const type = options?.type ?? \"human\";\n const identifier = options?.identifier;\n const subscribe = options?.subscribe;\n const silent = options?.silent ?? false;\n const authority = options?.authority;\n const participant: Participant = {\n id: participantId, name, status: \"online\", type,\n ...(identifier ? { identifier } : {}),\n ...(authority ? { authority } : {}),\n };\n this._participants.set(participantId, participant);\n\n // If already connected, disconnect the old channel first\n const existingChannel = this._channels.get(participantId);\n if (existingChannel) {\n existingChannel._markDisconnected();\n }\n\n const subscriptions = subscribe ?? new Set(ALL_CATEGORIES);\n const channel = new Channel(this, participantId, name, subscriptions);\n this._channels.set(participantId, channel);\n\n if (!silent) {\n const event = createEvent<ParticipantJoinedEvent>({\n type: \"ParticipantJoined\",\n category: \"PRESENCE\",\n room_id: this.roomId,\n participant_id: participantId,\n participant,\n });\n await this._storeAndBroadcast(event, participantId);\n }\n\n return channel;\n }\n\n /**\n * Observe all room events without being a participant.\n *\n * Returns a channel that receives every event — broadcasts AND targeted\n * @mention events directed at other participants. Observers do NOT appear\n * in `listParticipants()` and do not emit join/leave presence events,\n * since they are not participants.\n *\n * Disconnect via `observer.disconnect()` when done.\n *\n * @example\n * const observer = room.observe();\n * for await (const event of observer) {\n * // sees everything, including mentions for other participants\n * }\n */\n observe(): Channel {\n const id = `__obs_${this.roomId}_${this._nextObserverId++}`;\n const channel = new Channel(this, id, \"__observer__\", new Set(ALL_CATEGORIES));\n this._observers.add(channel);\n return channel;\n }\n\n // ── Read methods ───────────────────────────────────────────────────────────\n\n /**\n * Paginate messages, newest-first. Pass the returned `next_cursor` to get\n * the next (older) page.\n */\n async listMessages(\n limit = 30,\n cursor: string | null = null,\n ): Promise<PaginatedResult<Message>> {\n return this.storage.getMessages(this.roomId, limit, cursor);\n }\n\n /**\n * Full-text search across message content, newest-first.\n * `query` is matched case-insensitively against message content.\n */\n async searchMessages(\n query: string,\n limit = 10,\n cursor: string | null = null,\n ): Promise<PaginatedResult<Message>> {\n return this.storage.searchMessages(this.roomId, query, limit, cursor);\n }\n\n /** All currently connected participants (including agents). Observers excluded. */\n listParticipants(): Participant[] {\n return [...this._participants.values()];\n }\n\n /**\n * Paginate room events, newest-first.\n * `category` optionally filters to one EventCategory.\n */\n async listEvents(\n category: EventCategory | null = null,\n limit = 50,\n cursor: string | null = null,\n ): Promise<PaginatedResult<RoomEvent>> {\n return this.storage.getEvents(this.roomId, category, limit, cursor);\n }\n\n /** Look up a single message by ID. Returns null if not found. */\n async getMessage(id: string): Promise<Message | null> {\n return this.storage.getMessage(this.roomId, id);\n }\n\n /** Update a participant's authority level at runtime. */\n setParticipantAuthority(participantId: string, authority: AuthorityLevel): boolean {\n const participant = this._participants.get(participantId);\n if (!participant) return false;\n participant.authority = authority;\n return true;\n }\n\n // ── Internal methods (called by Channel) ──────────────────────────────────\n\n /**\n * @internal\n * Store a message, broadcast MessageSentEvent, and fire MentionedEvents.\n *\n * @mention scanning: looks for `@token` patterns in content and matches\n * against each connected participant's `identifier` and display `name`\n * (case-insensitive). Fires a `MentionedEvent` for each match, delivered\n * to the mentioned participant AND all observers.\n */\n async _handleMessage(message: Message): Promise<void> {\n await this.storage.addMessage(message);\n\n const event = createEvent<MessageSentEvent>({\n type: \"MessageSent\",\n category: \"MESSAGE\",\n room_id: this.roomId,\n participant_id: message.sender_id,\n message,\n });\n await this._storeAndBroadcast(event);\n\n const mentions = this._detectMentions(message.content);\n for (const mentionedId of mentions) {\n const ch = this._channels.get(mentionedId);\n if (ch) {\n const mentionEvent = createEvent<MentionedEvent>({\n type: \"Mentioned\",\n category: \"MENTION\",\n room_id: this.roomId,\n participant_id: mentionedId,\n message,\n });\n await this.storage.addEvent(mentionEvent);\n ch._deliver(mentionEvent);\n // Deliver mentions to all observers too\n for (const observer of this._observers) {\n observer._deliver(mentionEvent);\n }\n }\n }\n }\n\n /** @internal Store and broadcast an activity event. */\n async _handleEvent(event: RoomEvent): Promise<void> {\n await this._storeAndBroadcast(event, event.participant_id);\n }\n\n /** @internal Remove a channel and optionally broadcast ParticipantLeftEvent. */\n async _disconnectChannel(channel: Channel, silent = false): Promise<void> {\n // Observer channels are not participants — just remove from observer set\n if (this._observers.delete(channel)) {\n return;\n }\n\n const pid = channel.participantId;\n const participant = this._participants.get(pid);\n this._channels.delete(pid);\n this._participants.delete(pid);\n\n if (!silent && participant) {\n const event = createEvent<ParticipantLeftEvent>({\n type: \"ParticipantLeft\",\n category: \"PRESENCE\",\n room_id: this.roomId,\n participant_id: pid,\n participant,\n });\n await this._storeAndBroadcast(event);\n }\n }\n\n private async _storeAndBroadcast(\n event: RoomEvent,\n exclude?: string,\n ): Promise<void> {\n await this.storage.addEvent(event);\n this._broadcast(event, exclude);\n }\n\n private _broadcast(event: RoomEvent, exclude?: string): void {\n for (const [pid, channel] of this._channels) {\n if (pid !== exclude) {\n channel._deliver(event);\n }\n }\n for (const observer of this._observers) {\n observer._deliver(event);\n }\n }\n\n /**\n * Scan message content for `@token` patterns and return matching participant IDs.\n * Matches against both `identifier` (e.g. `@my-agent`) and display `name` (e.g. `@Alice`).\n * Case-insensitive. Deduplicates — each participant appears at most once.\n */\n private _detectMentions(content: string): string[] {\n const mentionedIds: string[] = [];\n const pattern = /@([a-zA-Z0-9_-]+)/g;\n let match;\n while ((match = pattern.exec(content)) !== null) {\n const token = match[1].toLowerCase();\n for (const [pid, participant] of this._participants) {\n const matchesId = participant.identifier?.toLowerCase() === token;\n const matchesName = participant.name.toLowerCase() === token;\n if ((matchesId || matchesName) && !mentionedIds.includes(pid)) {\n mentionedIds.push(pid);\n }\n }\n }\n return mentionedIds;\n }\n}\n","/** Random display name generation for participants and rooms. */\n\nconst PLACES = [\n \"bay\", \"cove\", \"glen\", \"moor\", \"fjord\", \"cape\", \"crag\", \"bluff\", \"cliff\", \"ridge\",\n \"peak\", \"mesa\", \"butte\", \"canyon\", \"gorge\", \"ravine\", \"gulch\", \"dell\", \"dune\", \"plain\",\n \"heath\", \"fell\", \"bog\", \"marsh\", \"pond\", \"lake\", \"tarn\", \"pool\", \"harbor\", \"haven\",\n \"inlet\", \"gulf\", \"sound\", \"strait\", \"channel\", \"delta\", \"lagoon\", \"atoll\", \"shoal\", \"shore\",\n \"coast\", \"isle\", \"forest\", \"grove\", \"copse\", \"glade\", \"meadow\", \"field\", \"valley\", \"hollow\",\n \"nook\", \"ford\", \"falls\", \"spring\", \"well\", \"crest\", \"knoll\", \"summit\", \"slope\", \"basin\",\n \"bank\", \"strand\", \"loch\", \"steppe\", \"tundra\", \"prairie\", \"savanna\", \"jungle\", \"desert\",\n \"highland\", \"estuary\", \"bight\", \"spit\", \"islet\", \"island\", \"tor\", \"vale\", \"brook\", \"creek\",\n \"river\", \"weir\", \"cascade\", \"scarp\", \"tower\", \"plateau\", \"upland\", \"lowland\",\n];\n\n/** Generate a random room name like \"glen-4827\". */\nexport function randomRoomName(): string {\n const place = PLACES[Math.floor(Math.random() * PLACES.length)];\n const digits = String(Math.floor(Math.random() * 9000) + 1000);\n return `${place}-${digits}`;\n}\n\nconst NAMES = [\n \"ash\", \"kai\", \"sol\", \"pip\", \"kit\", \"zev\", \"bly\", \"rue\", \"dex\", \"nix\",\n \"wren\", \"gray\", \"clay\", \"reed\", \"roux\", \"roan\", \"jade\", \"max\", \"val\", \"xen\",\n \"zen\", \"pax\", \"jude\", \"finn\", \"sage\", \"remy\", \"nico\", \"noel\", \"lumi\", \"jules\",\n \"hero\", \"eden\", \"blake\", \"bram\", \"clem\", \"flint\", \"nox\", \"oak\", \"moss\", \"bryn\",\n \"lyra\", \"mars\", \"neve\", \"onyx\", \"sable\", \"thea\", \"koa\", \"ren\", \"ora\", \"lev\",\n \"tru\", \"vox\", \"quinn\", \"rowan\", \"avery\", \"cass\", \"greer\", \"holt\", \"arlo\", \"drew\",\n \"emery\", \"finley\", \"harley\", \"harper\", \"jamie\", \"vesper\", \"west\", \"wynne\", \"yael\",\n \"zion\", \"sawyer\", \"scout\", \"tatum\", \"toby\", \"toni\", \"riley\", \"reese\", \"morgan\",\n \"micah\", \"logan\", \"lane\", \"jordan\", \"perry\", \"piper\", \"erin\", \"dylan\", \"camden\",\n \"seren\", \"elio\", \"cael\", \"davi\", \"lyric\", \"kiran\", \"arrow\", \"riven\", \"cleo\",\n \"sora\", \"tae\", \"cade\", \"milo\",\n];\n\n/** Generate a random display name like \"wren-4827\". */\nexport function randomName(): string {\n const name = NAMES[Math.floor(Math.random() * NAMES.length)];\n const digits = String(Math.floor(Math.random() * 9000) + 1000);\n return `${name}-${digits}`;\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/core/storage.ts","../src/core/channel.ts","../src/core/room.ts","../src/core/names.ts"],"sourcesContent":["/**\n * Storage protocol and reference implementations for stoops rooms.\n *\n * # Implementing StorageProtocol\n *\n * Provide your own implementation to persist messages and events to a real\n * database. Pass it to `new Room(roomId, myStorage)`.\n *\n * Pagination contract (applies to all paginated methods):\n * - Results are returned newest-first.\n * - `cursor` is the ID of the last item on the previous page (exclusive).\n * Pass `null` to start from the most recent.\n * - `next_cursor` in the result is the cursor to pass for the next (older) page.\n * - `has_more` is true if there are older items beyond the current page.\n *\n * @example\n * // Minimal Postgres implementation sketch:\n * class PostgresStorage implements StorageProtocol {\n * async addMessage(message) {\n * await db.query(\"INSERT INTO messages ...\", [message]);\n * return message;\n * }\n * async getMessage(room_id, message_id) {\n * return db.query(\"SELECT * FROM messages WHERE id = $1\", [message_id]);\n * }\n * async getMessages(room_id, limit = 30, cursor = null) {\n * // Fetch `limit` messages before `cursor`, newest-first\n * const rows = await db.query(\"...\");\n * return { items: rows, next_cursor: ..., has_more: ... };\n * }\n * async searchMessages(room_id, query, limit = 10, cursor = null) {\n * // Full-text search, newest-first\n * }\n * async addEvent(event) {\n * await db.query(\"INSERT INTO events ...\", [event]);\n * }\n * async getEvents(room_id, category = null, limit = 50, cursor = null) {\n * // Optional category filter, newest-first\n * }\n * }\n */\n\nimport type { RoomEvent } from \"./events.js\";\nimport type { EventCategory, Message, PaginatedResult } from \"./types.js\";\n\n// ── StorageProtocol ───────────────────────────────────────────────────────────\n\n/**\n * Persistence interface for a room's messages and events.\n *\n * Implement this to back rooms with a real database. The reference\n * `InMemoryStorage` is suitable for testing and single-process local use.\n *\n * All methods operate on a single `room_id` — one storage instance is shared\n * across all rooms (the `room_id` partitions the data).\n */\nexport interface StorageProtocol {\n /**\n * Persist a message and return it (with any server-assigned fields set).\n * Called automatically by `Channel.sendMessage()`.\n */\n addMessage(message: Message): Promise<Message>;\n\n /**\n * Look up a single message by ID. Returns null if not found.\n * Used by agents when resolving reply context and message refs.\n */\n getMessage(room_id: string, message_id: string): Promise<Message | null>;\n\n /**\n * Paginate messages for a room, newest-first.\n *\n * `cursor` — the `id` of the last message on the previous page (exclusive).\n * Pass null to start from the most recent message.\n */\n getMessages(\n room_id: string,\n limit?: number,\n cursor?: string | null,\n ): Promise<PaginatedResult<Message>>;\n\n /**\n * Full-text search across message content, newest-first.\n *\n * `query` — keyword or phrase to search for (case-insensitive).\n * `cursor` — pagination cursor (same semantics as `getMessages`).\n */\n searchMessages(\n room_id: string,\n query: string,\n limit?: number,\n cursor?: string | null,\n ): Promise<PaginatedResult<Message>>;\n\n /**\n * Persist a room event. Called for every event that passes through the room.\n * Events are append-only — never updated or deleted.\n */\n addEvent(event: RoomEvent): Promise<void>;\n\n /**\n * Paginate events for a room, newest-first.\n *\n * `category` — optional filter (e.g. EventCategory.MESSAGE). Pass null for all.\n * `cursor` — pagination cursor (index-based for events).\n */\n getEvents(\n room_id: string,\n category?: EventCategory | null,\n limit?: number,\n cursor?: string | null,\n ): Promise<PaginatedResult<RoomEvent>>;\n}\n\n// ── Pagination helpers (used by InMemoryStorage) ──────────────────────────────\n\n/**\n * Paginate an array by item ID cursor, returning results newest-first.\n * Items are assumed to be stored oldest-first (append order).\n *\n * @internal\n */\nexport function paginate<T>(\n items: T[],\n limit: number,\n cursor: string | null | undefined,\n key: (item: T) => string,\n): PaginatedResult<T> {\n let subset: T[];\n\n if (cursor != null) {\n const cursorIdx = items.findIndex((item) => key(item) === cursor);\n if (cursorIdx === -1) {\n return { items: [], next_cursor: null, has_more: false };\n }\n subset = items.slice(0, cursorIdx);\n } else {\n subset = items;\n }\n\n const page =\n limit < subset.length ? subset.slice(-limit) : subset.slice();\n page.reverse();\n const has_more = subset.length > limit;\n const next_cursor = has_more && page.length > 0 ? key(page[page.length - 1]) : null;\n\n return { items: page, next_cursor, has_more };\n}\n\n/**\n * Paginate an array by positional index cursor, returning results newest-first.\n * Used for events, which don't have stable IDs suitable for ID-based cursors.\n *\n * @internal\n */\nexport function paginateByIndex<T>(\n items: T[],\n limit: number,\n cursor: string | null | undefined,\n): PaginatedResult<T> {\n const parsedCursor = cursor != null ? parseInt(cursor, 10) : items.length;\n const endIdx = Number.isNaN(parsedCursor) ? items.length : parsedCursor;\n const startIdx = Math.max(0, endIdx - limit);\n const page = items.slice(startIdx, endIdx).reverse();\n const has_more = startIdx > 0;\n const next_cursor = has_more ? String(startIdx) : null;\n\n return { items: page, next_cursor, has_more };\n}\n\n// ── InMemoryStorage ───────────────────────────────────────────────────────────\n\n/**\n * Reference in-memory implementation of `StorageProtocol`.\n *\n * Suitable for tests, development, and single-process local use. All data is\n * lost on process restart — not for production.\n *\n * One instance can serve multiple rooms (data is partitioned by `room_id`).\n */\nexport class InMemoryStorage implements StorageProtocol {\n private _messages = new Map<string, Message[]>();\n private _events = new Map<string, RoomEvent[]>();\n\n async addMessage(message: Message): Promise<Message> {\n const list = this._messages.get(message.room_id) ?? [];\n list.push(message);\n this._messages.set(message.room_id, list);\n return message;\n }\n\n async getMessage(room_id: string, message_id: string): Promise<Message | null> {\n const list = this._messages.get(room_id) ?? [];\n return list.find((m) => m.id === message_id) ?? null;\n }\n\n async getMessages(\n room_id: string,\n limit = 30,\n cursor: string | null = null,\n ): Promise<PaginatedResult<Message>> {\n const messages = this._messages.get(room_id) ?? [];\n return paginate(messages, limit, cursor, (m) => m.id);\n }\n\n async searchMessages(\n room_id: string,\n query: string,\n limit = 10,\n cursor: string | null = null,\n ): Promise<PaginatedResult<Message>> {\n const messages = this._messages.get(room_id) ?? [];\n const q = query.toLowerCase();\n const filtered = messages.filter((m) =>\n m.content.toLowerCase().includes(q),\n );\n return paginate(filtered, limit, cursor, (m) => m.id);\n }\n\n async addEvent(event: RoomEvent): Promise<void> {\n const list = this._events.get(event.room_id) ?? [];\n list.push(event);\n this._events.set(event.room_id, list);\n }\n\n async getEvents(\n room_id: string,\n category: EventCategory | null = null,\n limit = 50,\n cursor: string | null = null,\n ): Promise<PaginatedResult<RoomEvent>> {\n let events = this._events.get(room_id) ?? [];\n if (category != null) {\n events = events.filter((e) => e.category === category);\n }\n return paginateByIndex(events, limit, cursor);\n }\n}\n","/**\n * Channel — a participant's bidirectional connection to a room.\n *\n * Created by `Room.connect()`. Never instantiated directly.\n *\n * # Sending\n * - `sendMessage()` — persist and broadcast a chat message\n * - `emit()` — push non-message events (tool use, mode changes, etc.)\n *\n * # Receiving\n * Channels are async-iterable — use `for await (const event of channel)` to\n * consume events. Only events in the channel's `subscriptions` set are\n * delivered. Alternatively, use `receive(timeoutMs)` for polling with a\n * timeout (used by EventMultiplexer).\n *\n * # Lifecycle\n * - `updateSubscriptions()` — change which EventCategories are delivered\n * - `disconnect(silent?)` — leave the room; pass `true` to suppress the\n * ParticipantLeft broadcast\n */\n\nimport type { RoomEvent } from \"./events.js\";\nimport type { EventCategory, Message } from \"./types.js\";\nimport { MessageSchema } from \"./types.js\";\nimport type { Room } from \"./room.js\";\n\ninterface Waiter {\n resolve: (event: RoomEvent) => void;\n reject: (err: Error) => void;\n}\n\nexport class Channel {\n readonly participantId: string;\n readonly participantName: string;\n subscriptions: Set<EventCategory>;\n\n private _room: Room;\n private _queue: RoomEvent[] = [];\n private _waiters: Waiter[] = [];\n private _disconnected = false;\n\n constructor(\n room: Room,\n participantId: string,\n participantName: string,\n subscriptions: Set<EventCategory>,\n ) {\n this._room = room;\n this.participantId = participantId;\n this.participantName = participantName;\n this.subscriptions = subscriptions;\n }\n\n get roomId(): string {\n return this._room.roomId;\n }\n\n /**\n * Send a chat message from this participant.\n *\n * Persists the message to storage, broadcasts a `MessageSentEvent` to all\n * participants (including the sender), and fires `MentionedEvent` for any\n * `@name` or `@identifier` patterns found in the content.\n *\n * @param content — message text (may be empty if image is provided)\n * @param replyToId — ID of the message being replied to (optional)\n * @param image — optional image attachment\n */\n async sendMessage(\n content: string,\n replyToId?: string | null,\n image?: {\n url: string;\n mimeType: string;\n sizeBytes: number;\n } | null,\n ): Promise<Message> {\n if (this._disconnected) {\n throw new Error(\"Channel is disconnected\");\n }\n const message = MessageSchema.parse({\n room_id: this._room.roomId,\n sender_id: this.participantId,\n sender_name: this.participantName,\n content,\n reply_to_id: replyToId ?? null,\n image_url: image?.url ?? null,\n image_mime_type: image?.mimeType ?? null,\n image_size_bytes: image?.sizeBytes ?? null,\n });\n await this._room._handleMessage(message);\n return message;\n }\n\n /**\n * Emit a non-message activity event to the room.\n *\n * Use this for platform events: tool use indicators, mode changes, compaction\n * notices, etc. The event is persisted and broadcast to all subscribed\n * participants.\n */\n async emit(event: RoomEvent): Promise<void> {\n if (this._disconnected) {\n throw new Error(\"Channel is disconnected\");\n }\n await this._room._handleEvent(event);\n }\n\n /**\n * Change which event categories this channel receives.\n * Takes effect immediately — buffered events from unsubscribed categories\n * are not retroactively removed.\n */\n updateSubscriptions(categories: Set<EventCategory>): void {\n this.subscriptions = categories;\n }\n\n /**\n * Leave the room.\n *\n * @param silent — if true, suppresses the `ParticipantLeft` broadcast.\n * Agents disconnect silently to avoid chat noise.\n */\n async disconnect(silent = false): Promise<void> {\n if (!this._disconnected) {\n this._disconnected = true;\n // Wake pending waiters so async iterators exit cleanly\n const waiters = this._waiters;\n this._waiters = [];\n for (const w of waiters) {\n w.reject(new Error(\"Channel disconnected\"));\n }\n await this._room._disconnectChannel(this, silent);\n }\n }\n\n /** @internal Called by Room to mark this channel as disconnected without removing from room maps. */\n _markDisconnected(): void {\n if (!this._disconnected) {\n this._disconnected = true;\n const waiters = this._waiters;\n this._waiters = [];\n for (const w of waiters) {\n w.reject(new Error(\"Channel disconnected\"));\n }\n }\n }\n\n /** @internal Called by Room to deliver an incoming event. Filters by subscription. */\n _deliver(event: RoomEvent): void {\n if (this._disconnected) return;\n if (!this.subscriptions.has(event.category)) return;\n\n if (this._waiters.length > 0) {\n const waiter = this._waiters.shift()!;\n waiter.resolve(event);\n } else {\n this._queue.push(event);\n }\n }\n\n /**\n * Receive the next event, waiting up to `timeoutMs`.\n *\n * Returns null if no event arrives within the timeout. Drains buffered events\n * before waiting. Used by `EventMultiplexer` to fan-in events from multiple\n * rooms into a single stream.\n */\n receive(timeoutMs: number): Promise<RoomEvent | null> {\n if (this._queue.length > 0) {\n return Promise.resolve(this._queue.shift()!);\n }\n if (this._disconnected) {\n return Promise.resolve(null);\n }\n\n return new Promise<RoomEvent | null>((resolve) => {\n let settled = false;\n const waiter: Waiter = {\n resolve: (event) => {\n if (!settled) {\n settled = true;\n clearTimeout(timer);\n resolve(event);\n }\n },\n reject: () => {\n if (!settled) {\n settled = true;\n clearTimeout(timer);\n resolve(null);\n }\n },\n };\n this._waiters.push(waiter);\n\n const timer = setTimeout(() => {\n if (!settled) {\n settled = true;\n const idx = this._waiters.indexOf(waiter);\n if (idx !== -1) this._waiters.splice(idx, 1);\n resolve(null);\n }\n }, timeoutMs);\n });\n }\n\n /**\n * Async iterator — yields events as they arrive.\n *\n * Used by `EventMultiplexer` to fan-in all room channels into a single stream.\n * The iterator completes when the channel is disconnected.\n *\n * @example\n * for await (const event of channel) {\n * console.log(event.type);\n * }\n */\n [Symbol.asyncIterator](): AsyncIterator<RoomEvent> {\n return {\n next: (): Promise<IteratorResult<RoomEvent>> => {\n if (this._queue.length > 0) {\n return Promise.resolve({\n value: this._queue.shift()!,\n done: false,\n });\n }\n\n if (this._disconnected) {\n return Promise.resolve({\n value: undefined as unknown as RoomEvent,\n done: true,\n });\n }\n\n return new Promise<IteratorResult<RoomEvent>>((resolve, reject) => {\n this._waiters.push({\n resolve: (event) => resolve({ value: event, done: false }),\n reject,\n });\n });\n },\n };\n }\n}\n","/**\n * Room — a shared chat space where humans and agents are all just participants.\n *\n * Transport-agnostic: no WebSockets, no HTTP. The caller owns the transport\n * and passes messages/events in via channels. This means the same Room works\n * identically in a CLI, a web server, or a test.\n *\n * # Connecting\n * Participants connect via `room.connect()`, which returns a `Channel`. The\n * channel is their bidirectional connection: they send messages and receive\n * events through it.\n *\n * # Observing\n * Call `room.observe()` to get a read-only-style channel that receives every\n * event in the room — including targeted @mention events directed at other\n * participants. Observers are NOT participants: they don't appear in\n * `listParticipants()` and don't trigger join/leave events.\n *\n * # @mention detection\n * When a message is sent, the Room scans its content for `@token` patterns and\n * fires a `MentionedEvent` for any participant whose `identifier` or display\n * `name` matches the token (case-insensitive). The mention event is delivered\n * to the mentioned participant AND to all observers.\n *\n * @example\n * const storage = new InMemoryStorage();\n * const room = new Room(\"room-1\", storage);\n *\n * const aliceChannel = await room.connect(\"alice-id\", \"Alice\");\n * const agentChannel = await room.connect(\"agent-id\", \"Agent\", \"agent\", \"my-agent\");\n * const observer = room.observe();\n *\n * await aliceChannel.sendMessage(\"hey @my-agent what do you think?\");\n * // → MessageSentEvent broadcast to all participants + observer\n * // → MentionedEvent delivered to agentChannel + observer\n */\n\nimport { Channel } from \"./channel.js\";\nimport { createEvent } from \"./events.js\";\nimport type {\n MentionedEvent,\n MessageSentEvent,\n ParticipantJoinedEvent,\n ParticipantLeftEvent,\n RoomEvent,\n} from \"./events.js\";\nimport { InMemoryStorage, type StorageProtocol } from \"./storage.js\";\nimport { EventCategory, type AuthorityLevel, type Message, type PaginatedResult, type Participant, type ParticipantType } from \"./types.js\";\n\nconst ALL_CATEGORIES = new Set<EventCategory>([\n EventCategory.MESSAGE,\n EventCategory.PRESENCE,\n EventCategory.ACTIVITY,\n EventCategory.MENTION,\n]);\n\nexport class Room {\n readonly roomId: string;\n /** Direct access to the underlying storage. Useful for bulk reads. */\n readonly storage: StorageProtocol;\n private _channels = new Map<string, Channel>();\n private _participants = new Map<string, Participant>();\n private _observers = new Set<Channel>();\n private _nextObserverId = 0;\n\n /**\n * @param roomId — stable identifier for this room (e.g. a UUID or slug)\n * @param storage — storage backend; defaults to `InMemoryStorage`\n */\n constructor(roomId: string, storage?: StorageProtocol) {\n this.roomId = roomId;\n this.storage = storage ?? new InMemoryStorage();\n }\n\n /**\n * Connect a participant and return their channel.\n */\n async connect(\n participantId: string,\n name: string,\n options?: {\n type?: ParticipantType;\n identifier?: string;\n subscribe?: Set<EventCategory>;\n silent?: boolean;\n authority?: AuthorityLevel;\n },\n ): Promise<Channel> {\n const type = options?.type ?? \"human\";\n const identifier = options?.identifier;\n const subscribe = options?.subscribe;\n const silent = options?.silent ?? false;\n const authority = options?.authority;\n const participant: Participant = {\n id: participantId, name, status: \"online\", type,\n ...(identifier ? { identifier } : {}),\n ...(authority ? { authority } : {}),\n };\n this._participants.set(participantId, participant);\n\n // If already connected, disconnect the old channel first\n const existingChannel = this._channels.get(participantId);\n if (existingChannel) {\n existingChannel._markDisconnected();\n }\n\n const subscriptions = subscribe ?? new Set(ALL_CATEGORIES);\n const channel = new Channel(this, participantId, name, subscriptions);\n this._channels.set(participantId, channel);\n\n if (!silent) {\n const event = createEvent<ParticipantJoinedEvent>({\n type: \"ParticipantJoined\",\n category: \"PRESENCE\",\n room_id: this.roomId,\n participant_id: participantId,\n participant,\n });\n await this._storeAndBroadcast(event, participantId);\n }\n\n return channel;\n }\n\n /**\n * Observe all room events without being a participant.\n *\n * Returns a channel that receives every event — broadcasts AND targeted\n * @mention events directed at other participants. Observers do NOT appear\n * in `listParticipants()` and do not emit join/leave presence events,\n * since they are not participants.\n *\n * Disconnect via `observer.disconnect()` when done.\n *\n * @example\n * const observer = room.observe();\n * for await (const event of observer) {\n * // sees everything, including mentions for other participants\n * }\n */\n observe(): Channel {\n const id = `__obs_${this.roomId}_${this._nextObserverId++}`;\n const channel = new Channel(this, id, \"__observer__\", new Set(ALL_CATEGORIES));\n this._observers.add(channel);\n return channel;\n }\n\n // ── Read methods ───────────────────────────────────────────────────────────\n\n /**\n * Paginate messages, newest-first. Pass the returned `next_cursor` to get\n * the next (older) page.\n */\n async listMessages(\n limit = 30,\n cursor: string | null = null,\n ): Promise<PaginatedResult<Message>> {\n return this.storage.getMessages(this.roomId, limit, cursor);\n }\n\n /**\n * Full-text search across message content, newest-first.\n * `query` is matched case-insensitively against message content.\n */\n async searchMessages(\n query: string,\n limit = 10,\n cursor: string | null = null,\n ): Promise<PaginatedResult<Message>> {\n return this.storage.searchMessages(this.roomId, query, limit, cursor);\n }\n\n /** All currently connected participants (including agents). Observers excluded. */\n listParticipants(): Participant[] {\n return [...this._participants.values()];\n }\n\n /**\n * Paginate room events, newest-first.\n * `category` optionally filters to one EventCategory.\n */\n async listEvents(\n category: EventCategory | null = null,\n limit = 50,\n cursor: string | null = null,\n ): Promise<PaginatedResult<RoomEvent>> {\n return this.storage.getEvents(this.roomId, category, limit, cursor);\n }\n\n /** Look up a single message by ID. Returns null if not found. */\n async getMessage(id: string): Promise<Message | null> {\n return this.storage.getMessage(this.roomId, id);\n }\n\n /** Update a participant's authority level at runtime. */\n setParticipantAuthority(participantId: string, authority: AuthorityLevel): boolean {\n const participant = this._participants.get(participantId);\n if (!participant) return false;\n participant.authority = authority;\n return true;\n }\n\n // ── Internal methods (called by Channel) ──────────────────────────────────\n\n /**\n * @internal\n * Store a message, broadcast MessageSentEvent, and fire MentionedEvents.\n *\n * @mention scanning: looks for `@token` patterns in content and matches\n * against each connected participant's `identifier` and display `name`\n * (case-insensitive). Fires a `MentionedEvent` for each match, delivered\n * to the mentioned participant AND all observers.\n */\n async _handleMessage(message: Message): Promise<void> {\n await this.storage.addMessage(message);\n\n const event = createEvent<MessageSentEvent>({\n type: \"MessageSent\",\n category: \"MESSAGE\",\n room_id: this.roomId,\n participant_id: message.sender_id,\n message,\n });\n await this._storeAndBroadcast(event);\n\n const mentions = this._detectMentions(message.content);\n for (const mentionedId of mentions) {\n const ch = this._channels.get(mentionedId);\n if (ch) {\n const mentionEvent = createEvent<MentionedEvent>({\n type: \"Mentioned\",\n category: \"MENTION\",\n room_id: this.roomId,\n participant_id: mentionedId,\n message,\n });\n await this.storage.addEvent(mentionEvent);\n ch._deliver(mentionEvent);\n // Deliver mentions to all observers too\n for (const observer of this._observers) {\n observer._deliver(mentionEvent);\n }\n }\n }\n }\n\n /** @internal Store and broadcast an activity event. */\n async _handleEvent(event: RoomEvent): Promise<void> {\n await this._storeAndBroadcast(event, event.participant_id);\n }\n\n /** @internal Remove a channel and optionally broadcast ParticipantLeftEvent. */\n async _disconnectChannel(channel: Channel, silent = false): Promise<void> {\n // Observer channels are not participants — just remove from observer set\n if (this._observers.delete(channel)) {\n return;\n }\n\n const pid = channel.participantId;\n const participant = this._participants.get(pid);\n this._channels.delete(pid);\n this._participants.delete(pid);\n\n if (!silent && participant) {\n const event = createEvent<ParticipantLeftEvent>({\n type: \"ParticipantLeft\",\n category: \"PRESENCE\",\n room_id: this.roomId,\n participant_id: pid,\n participant,\n });\n await this._storeAndBroadcast(event);\n }\n }\n\n private async _storeAndBroadcast(\n event: RoomEvent,\n exclude?: string,\n ): Promise<void> {\n await this.storage.addEvent(event);\n this._broadcast(event, exclude);\n }\n\n private _broadcast(event: RoomEvent, exclude?: string): void {\n for (const [pid, channel] of this._channels) {\n if (pid !== exclude) {\n channel._deliver(event);\n }\n }\n for (const observer of this._observers) {\n observer._deliver(event);\n }\n }\n\n /**\n * Scan message content for `@token` patterns and return matching participant IDs.\n * Matches against both `identifier` (e.g. `@my-agent`) and display `name` (e.g. `@Alice`).\n * Case-insensitive. Deduplicates — each participant appears at most once.\n */\n private _detectMentions(content: string): string[] {\n const mentionedIds: string[] = [];\n const pattern = /@([a-zA-Z0-9_-]+)/g;\n let match;\n while ((match = pattern.exec(content)) !== null) {\n const token = match[1].toLowerCase();\n for (const [pid, participant] of this._participants) {\n const matchesId = participant.identifier?.toLowerCase() === token;\n const matchesName = participant.name.toLowerCase() === token;\n if ((matchesId || matchesName) && !mentionedIds.includes(pid)) {\n mentionedIds.push(pid);\n }\n }\n }\n return mentionedIds;\n }\n}\n","/** Random display name generation for participants and rooms. */\n\nconst PLACES = [\n \"bay\", \"cove\", \"glen\", \"moor\", \"fjord\", \"cape\", \"crag\", \"bluff\", \"cliff\", \"ridge\",\n \"peak\", \"mesa\", \"butte\", \"canyon\", \"gorge\", \"ravine\", \"gulch\", \"dell\", \"dune\", \"plain\",\n \"heath\", \"fell\", \"bog\", \"marsh\", \"pond\", \"lake\", \"tarn\", \"pool\", \"harbor\", \"haven\",\n \"inlet\", \"gulf\", \"sound\", \"strait\", \"channel\", \"delta\", \"lagoon\", \"atoll\", \"shoal\", \"shore\",\n \"coast\", \"isle\", \"forest\", \"grove\", \"copse\", \"glade\", \"meadow\", \"field\", \"valley\", \"hollow\",\n \"nook\", \"ford\", \"falls\", \"spring\", \"well\", \"crest\", \"knoll\", \"summit\", \"slope\", \"basin\",\n \"bank\", \"strand\", \"loch\", \"steppe\", \"tundra\", \"prairie\", \"savanna\", \"jungle\", \"desert\",\n \"highland\", \"estuary\", \"bight\", \"spit\", \"islet\", \"island\", \"tor\", \"vale\", \"brook\", \"creek\",\n \"river\", \"weir\", \"cascade\", \"scarp\", \"tower\", \"plateau\", \"upland\", \"lowland\",\n];\n\n/** Generate a random room name like \"Glen-4827\". */\nexport function randomRoomName(): string {\n const place = PLACES[Math.floor(Math.random() * PLACES.length)];\n const digits = String(Math.floor(Math.random() * 9000) + 1000);\n return `${place[0].toUpperCase()}${place.slice(1)}-${digits}`;\n}\n\nconst NAMES = [\n \"ash\", \"kai\", \"sol\", \"pip\", \"kit\", \"zev\", \"bly\", \"rue\", \"dex\", \"nix\",\n \"wren\", \"gray\", \"clay\", \"reed\", \"roux\", \"roan\", \"jade\", \"max\", \"val\", \"xen\",\n \"zen\", \"pax\", \"jude\", \"finn\", \"sage\", \"remy\", \"nico\", \"noel\", \"lumi\", \"jules\",\n \"hero\", \"eden\", \"blake\", \"bram\", \"clem\", \"flint\", \"nox\", \"oak\", \"moss\", \"bryn\",\n \"lyra\", \"mars\", \"neve\", \"onyx\", \"sable\", \"thea\", \"koa\", \"ren\", \"ora\", \"lev\",\n \"tru\", \"vox\", \"quinn\", \"rowan\", \"avery\", \"cass\", \"greer\", \"holt\", \"arlo\", \"drew\",\n \"emery\", \"finley\", \"harley\", \"harper\", \"jamie\", \"vesper\", \"west\", \"wynne\", \"yael\",\n \"zion\", \"sawyer\", \"scout\", \"tatum\", \"toby\", \"toni\", \"riley\", \"reese\", \"morgan\",\n \"micah\", \"logan\", \"lane\", \"jordan\", \"perry\", \"piper\", \"erin\", \"dylan\", \"camden\",\n \"seren\", \"elio\", \"cael\", \"davi\", \"lyric\", \"kiran\", \"arrow\", \"riven\", \"cleo\",\n \"sora\", \"tae\", \"cade\", \"milo\",\n];\n\n/** Generate a random display name like \"Wren-4827\". */\nexport function randomName(): string {\n const name = NAMES[Math.floor(Math.random() * NAMES.length)];\n const digits = String(Math.floor(Math.random() * 9000) + 1000);\n return `${name[0].toUpperCase()}${name.slice(1)}-${digits}`;\n}\n"],"mappings":"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","names":[]}
|
|
@@ -5,10 +5,10 @@ import {
|
|
|
5
5
|
StoopsEngagement,
|
|
6
6
|
buildCatchUpLines,
|
|
7
7
|
formatEvent
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-66EFQ2XO.js";
|
|
9
9
|
import {
|
|
10
10
|
createEvent
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-EPLQQF6S.js";
|
|
12
12
|
|
|
13
13
|
// src/agent/room-data-source.ts
|
|
14
14
|
var LocalRoomDataSource = class {
|
|
@@ -965,4 +965,4 @@ export {
|
|
|
965
965
|
RemoteRoomDataSource,
|
|
966
966
|
SseMultiplexer
|
|
967
967
|
};
|
|
968
|
-
//# sourceMappingURL=chunk-
|
|
968
|
+
//# sourceMappingURL=chunk-OA3CODNP.js.map
|
package/dist/claude/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { I as ILLMSession, R as RoomResolver, C as ClaudeSessionOptions, a as ContentPart } from '../types-
|
|
2
|
-
import '../index-
|
|
1
|
+
import { I as ILLMSession, R as RoomResolver, C as ClaudeSessionOptions, a as ContentPart } from '../types-9iTDVOJG.js';
|
|
2
|
+
import '../index-ByKHLUOe.js';
|
|
3
3
|
import 'zod';
|
|
4
4
|
|
|
5
5
|
/** Claude Agent SDK session backend for stoops agents. */
|
package/dist/claude/index.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import {
|
|
2
2
|
createFullMcpServer
|
|
3
|
-
} from "../chunk-
|
|
3
|
+
} from "../chunk-B4LBE5QS.js";
|
|
4
4
|
import {
|
|
5
5
|
contentPartsToString
|
|
6
|
-
} from "../chunk-
|
|
7
|
-
import "../chunk-
|
|
6
|
+
} from "../chunk-66EFQ2XO.js";
|
|
7
|
+
import "../chunk-EPLQQF6S.js";
|
|
8
8
|
|
|
9
9
|
// src/claude/session.ts
|
|
10
10
|
import { tmpdir } from "os";
|