@zooid/transport-matrix 0.12.0 → 0.13.0

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/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/matrix-client.ts","../src/context-provider.ts","../src/registration.ts","../src/identity.ts","../src/mentions.ts","../src/router.ts","../src/space-provisioner.ts","../src/bot-pool.ts","../src/transport.ts","../src/session-keys.ts","../src/event-encoders.ts","../src/markdown-to-matrix-html.ts","../src/pending-media.ts","../src/media-client.ts","../src/attachments.ts","../src/sync-loop.ts","../src/workforce-publisher.ts"],"sourcesContent":["import { randomUUID } from 'node:crypto'\n\nexport interface MatrixClientOptions {\n homeserver: string\n asToken: string\n fetch?: typeof globalThis.fetch\n}\n\nexport interface SendMessageInput {\n roomId: string\n asUserId: string\n content: { msgtype: string; body: string; [k: string]: unknown }\n threadRoot?: string\n}\n\nexport interface SendCustomEventInput {\n roomId: string\n asUserId: string\n eventType: string\n content: Record<string, unknown>\n}\n\nexport interface SetTypingInput {\n roomId: string\n asUserId: string\n typing: boolean\n /** ms — homeserver expects re-PUTs before this expires. Ignored when typing=false. */\n timeoutMs?: number\n}\n\nexport interface SetPresenceInput {\n asUserId: string\n presence: 'online' | 'unavailable' | 'offline'\n statusMsg?: string\n}\n\nexport class MatrixClient {\n private readonly homeserver: string\n private readonly asToken: string\n private readonly fetch: typeof globalThis.fetch\n\n constructor(opts: MatrixClientOptions) {\n this.homeserver = opts.homeserver.replace(/\\/$/, '')\n this.asToken = opts.asToken\n this.fetch = opts.fetch ?? globalThis.fetch\n }\n\n async registerBot(\n localpart: string,\n ): Promise<{ user_id: string; device_id: string } | undefined> {\n const r = await this.fetch(`${this.homeserver}/_matrix/client/v3/register`, {\n method: 'POST',\n headers: { Authorization: `Bearer ${this.asToken}` },\n body: JSON.stringify({ type: 'm.login.application_service', username: localpart }),\n })\n if (r.status === 200) return (await r.json()) as { user_id: string; device_id: string }\n if (r.status === 400) {\n const body = (await r.json()) as { errcode?: string }\n if (body.errcode === 'M_USER_IN_USE') return undefined\n }\n throw new Error(`registerBot(${localpart}) failed: ${r.status}`)\n }\n\n async resolveAlias(alias: string): Promise<string | null> {\n const r = await this.fetch(\n `${this.homeserver}/_matrix/client/v3/directory/room/${encodeURIComponent(alias)}`,\n { headers: { Authorization: `Bearer ${this.asToken}` } },\n )\n if (r.status === 404) return null\n if (!r.ok) throw new Error(`resolveAlias(${alias}) failed: ${r.status}`)\n const j = (await r.json()) as { room_id: string }\n return j.room_id\n }\n\n async createRoom(opts: {\n roomAliasName: string\n invite: string[]\n senderUserId: string\n preset?: 'public_chat' | 'private_chat' | 'trusted_private_chat'\n /** Optional `m.room.name`. When set, sent in the createRoom body so the\n * room has a display name from the moment it exists. */\n name?: string\n /** When set, the room is created with a `restricted` join rule whose allow\n * condition references this space room ID — i.e. joinable by space members\n * only, rather than the whole homeserver. */\n restrictedToSpaceId?: string\n /** Explicit room version. Restricted join rules require v8+; omit to use the\n * homeserver default (modern Tuwunel defaults to v10/v11). */\n roomVersion?: string\n /**\n * Seeds `m.room.power_levels.users` at creation via\n * `power_level_content_override.users`. The caller owns the full map —\n * typically the AS bot at 100, plus operator and any agents with\n * declared PLs. Empty/absent → no override (the preset's defaults apply).\n */\n userPowerLevels?: Record<string, number>\n }): Promise<string> {\n const body: Record<string, unknown> = {\n room_alias_name: opts.roomAliasName,\n invite: opts.invite,\n preset: opts.preset ?? 'public_chat',\n }\n if (opts.name !== undefined) body.name = opts.name\n if (opts.roomVersion !== undefined) body.room_version = opts.roomVersion\n if (opts.restrictedToSpaceId !== undefined) {\n body.initial_state = [\n {\n type: 'm.room.join_rules',\n state_key: '',\n content: {\n join_rule: 'restricted',\n allow: [{ type: 'm.room_membership', room_id: opts.restrictedToSpaceId }],\n },\n },\n ]\n }\n if (opts.userPowerLevels && Object.keys(opts.userPowerLevels).length > 0) {\n // `power_level_content_override.users` REPLACES the default `{ creator: 100 }`\n // map rather than merging into it. If the creator (senderUserId) is omitted\n // here they drop to `users_default` (0) and can't set `m.room.canonical_alias`\n // (PL 50) during creation — the createRoom call then 403s and leaves an\n // aliasless orphan room. Always keep the creator at admin power.\n const users = { ...opts.userPowerLevels }\n if (users[opts.senderUserId] === undefined) users[opts.senderUserId] = 100\n body.power_level_content_override = { users }\n }\n const r = await this.fetch(\n `${this.homeserver}/_matrix/client/v3/createRoom?user_id=${encodeURIComponent(opts.senderUserId)}`,\n {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${this.asToken}`,\n 'content-type': 'application/json',\n },\n body: JSON.stringify(body),\n },\n )\n if (!r.ok) {\n const body = await r.text()\n throw new Error(`createRoom(${opts.roomAliasName}) failed: ${r.status} ${body}`)\n }\n const j = (await r.json()) as { room_id: string }\n return j.room_id\n }\n\n async createRoomRaw(opts: {\n asUserId: string\n body: Record<string, unknown>\n }): Promise<string> {\n const url = `${this.homeserver}/_matrix/client/v3/createRoom?user_id=${encodeURIComponent(opts.asUserId)}`\n const r = await this.fetch(url, {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${this.asToken}`,\n 'content-type': 'application/json',\n },\n body: JSON.stringify(opts.body),\n })\n if (!r.ok) throw new Error(`createRoomRaw failed: ${r.status}`)\n const j = (await r.json()) as { room_id: string }\n return j.room_id\n }\n\n async sendStateEvent(opts: {\n roomId: string\n asUserId: string\n eventType: string\n stateKey?: string\n content: Record<string, unknown>\n }): Promise<{ event_id: string }> {\n const url =\n `${this.homeserver}/_matrix/client/v3/rooms/${encodeURIComponent(opts.roomId)}` +\n `/state/${encodeURIComponent(opts.eventType)}/${encodeURIComponent(opts.stateKey ?? '')}` +\n `?user_id=${encodeURIComponent(opts.asUserId)}`\n const r = await this.fetch(url, {\n method: 'PUT',\n headers: {\n Authorization: `Bearer ${this.asToken}`,\n 'content-type': 'application/json',\n },\n body: JSON.stringify(opts.content),\n })\n if (!r.ok) throw new Error(`sendStateEvent ${opts.eventType} failed: ${r.status}`)\n return (await r.json()) as { event_id: string }\n }\n\n /**\n * Invite a user to a room. Sent as the inviter (`asUserId`) — that user\n * needs invite power in the room. Tolerates the \"already in room /\n * already invited\" responses idempotently so bootstrap can run on a\n * fresh AND a populated homeserver without branching.\n */\n async invite(opts: {\n roomId: string\n asUserId: string\n targetUserId: string\n }): Promise<void> {\n const url =\n `${this.homeserver}/_matrix/client/v3/rooms/${encodeURIComponent(opts.roomId)}/invite` +\n `?user_id=${encodeURIComponent(opts.asUserId)}`\n const r = await this.fetch(url, {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${this.asToken}`,\n 'content-type': 'application/json',\n },\n body: JSON.stringify({ user_id: opts.targetUserId }),\n })\n if (r.ok) return\n if (r.status === 403) {\n // Tuwunel/Synapse use M_FORBIDDEN both for permission errors AND for\n // \"already a member / already invited\". Inspect the body so we only\n // swallow the idempotent case.\n //\n // Phrasings seen in the wild:\n // - Synapse: \"is already in the room\", \"is already invited\"\n // - Tuwunel: \"cannot invite user that is joined or banned\" (one\n // string for both \"joined\" and \"banned\" — we can't tell which\n // from the body; the bot-pool's outer try-catch surfaces a real\n // ban via the subsequent joinRoom failure)\n // - Generic: \"X is already a member of the room\"\n const body = await r.text()\n const idempotent =\n /already (in the room|invited|a member|joined)/i.test(body) ||\n /user that is joined/i.test(body)\n if (idempotent) return\n throw new Error(`invite(${opts.targetUserId}) failed: 403 ${body}`)\n }\n throw new Error(`invite(${opts.targetUserId}) failed: ${r.status}`)\n }\n\n async leaveRoom(\n roomId: string,\n asUserId: string,\n opts?: { reason?: string },\n ): Promise<void> {\n const url =\n `${this.homeserver}/_matrix/client/v3/rooms/${encodeURIComponent(roomId)}/leave` +\n `?user_id=${encodeURIComponent(asUserId)}`\n const r = await this.fetch(url, {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${this.asToken}`,\n 'content-type': 'application/json',\n },\n body: JSON.stringify(opts?.reason ? { reason: opts.reason } : {}),\n })\n if (!r.ok) throw new Error(`leaveRoom(${roomId}, ${asUserId}) failed: ${r.status}`)\n }\n\n async joinRoom(roomIdOrAlias: string, asUserId: string): Promise<void> {\n const url =\n `${this.homeserver}/_matrix/client/v3/join/${encodeURIComponent(roomIdOrAlias)}` +\n `?user_id=${encodeURIComponent(asUserId)}`\n const r = await this.fetch(url, {\n method: 'POST',\n headers: { Authorization: `Bearer ${this.asToken}` },\n body: '{}',\n })\n if (!r.ok) throw new Error(`joinRoom(${roomIdOrAlias}) failed: ${r.status}`)\n }\n\n async sendMessage(input: SendMessageInput): Promise<{ event_id: string }> {\n const content: Record<string, unknown> = { ...input.content }\n if (input.threadRoot) {\n content['m.relates_to'] = { rel_type: 'm.thread', event_id: input.threadRoot }\n }\n return this.sendEvent(input.roomId, input.asUserId, 'm.room.message', content)\n }\n\n async sendCustomEvent(input: SendCustomEventInput): Promise<{ event_id: string }> {\n return this.sendEvent(input.roomId, input.asUserId, input.eventType, input.content)\n }\n\n async setTyping(input: SetTypingInput): Promise<void> {\n const url =\n `${this.homeserver}/_matrix/client/v3/rooms/${encodeURIComponent(input.roomId)}` +\n `/typing/${encodeURIComponent(input.asUserId)}` +\n `?user_id=${encodeURIComponent(input.asUserId)}`\n const body: Record<string, unknown> = { typing: input.typing }\n if (input.typing && input.timeoutMs !== undefined) body.timeout = input.timeoutMs\n const r = await this.fetch(url, {\n method: 'PUT',\n headers: { Authorization: `Bearer ${this.asToken}` },\n body: JSON.stringify(body),\n })\n if (!r.ok) throw new Error(`setTyping(${input.roomId}, ${input.asUserId}) failed: ${r.status}`)\n }\n\n async setDisplayName(asUserId: string, displayName: string): Promise<void> {\n const url =\n `${this.homeserver}/_matrix/client/v3/profile/${encodeURIComponent(asUserId)}/displayname` +\n `?user_id=${encodeURIComponent(asUserId)}`\n const r = await this.fetch(url, {\n method: 'PUT',\n headers: {\n Authorization: `Bearer ${this.asToken}`,\n 'content-type': 'application/json',\n },\n body: JSON.stringify({ displayname: displayName }),\n })\n if (!r.ok) throw new Error(`setDisplayName(${asUserId}) failed: ${r.status}`)\n }\n\n async setPresence(input: SetPresenceInput): Promise<void> {\n const url =\n `${this.homeserver}/_matrix/client/v3/presence/${encodeURIComponent(input.asUserId)}/status` +\n `?user_id=${encodeURIComponent(input.asUserId)}`\n const body: Record<string, unknown> = { presence: input.presence }\n if (input.statusMsg !== undefined) body.status_msg = input.statusMsg\n const r = await this.fetch(url, {\n method: 'PUT',\n headers: { Authorization: `Bearer ${this.asToken}` },\n body: JSON.stringify(body),\n })\n if (!r.ok) throw new Error(`setPresence(${input.asUserId}) failed: ${r.status}`)\n }\n\n /**\n * Fetch a single event from a room. Used to recover thread-root context\n * after a daemon restart wipes the in-memory threadStates cache.\n */\n async fetchEvent(\n roomId: string,\n eventId: string,\n asUserId: string,\n ): Promise<Record<string, unknown> | null> {\n const url =\n `${this.homeserver}/_matrix/client/v3/rooms/${encodeURIComponent(roomId)}` +\n `/event/${encodeURIComponent(eventId)}` +\n `?user_id=${encodeURIComponent(asUserId)}`\n const r = await this.fetch(url, {\n method: 'GET',\n headers: { Authorization: `Bearer ${this.asToken}` },\n })\n if (r.status === 404) return null\n if (!r.ok) throw new Error(`fetchEvent(${eventId}) failed: ${r.status}`)\n return (await r.json()) as Record<string, unknown>\n }\n\n /**\n * Fetch replies to a thread root via the relations endpoint, oldest-first.\n * Pass `limit` and `from` for pagination; `next_batch` echoes back when\n * there are more replies. Returns `{ chunk: [] }` when the root is unknown.\n */\n async fetchThreadRelations(opts: {\n roomId: string\n rootEventId: string\n asUserId: string\n limit?: number\n from?: string\n }): Promise<{ chunk: Array<Record<string, unknown>>; next_batch?: string }> {\n const params = new URLSearchParams({\n dir: 'f',\n limit: String(opts.limit ?? 100),\n user_id: opts.asUserId,\n })\n if (opts.from) params.set('from', opts.from)\n const url =\n `${this.homeserver}/_matrix/client/v1/rooms/${encodeURIComponent(opts.roomId)}` +\n `/relations/${encodeURIComponent(opts.rootEventId)}/m.thread?${params.toString()}`\n const r = await this.fetch(url, {\n method: 'GET',\n headers: { Authorization: `Bearer ${this.asToken}` },\n })\n if (r.status === 404) return { chunk: [] }\n if (!r.ok) throw new Error(`fetchThreadRelations(${opts.rootEventId}) failed: ${r.status}`)\n const body = (await r.json()) as {\n chunk?: Array<Record<string, unknown>>\n next_batch?: string\n }\n return { chunk: body.chunk ?? [], next_batch: body.next_batch }\n }\n\n /**\n * Paginate the room timeline. Returns events newest-first (dir=b) per Matrix\n * spec. The caller is responsible for reversing if it wants oldest-first.\n */\n async fetchRoomMessages(opts: {\n roomId: string\n asUserId: string\n limit?: number\n /** Opaque pagination token returned in `end` from a previous call. */\n from?: string\n /**\n * Server-side RoomEventFilter. Common keys: `types` (whitelist event types),\n * `not_types`, `not_rel_types` (e.g. `['m.thread']` to exclude thread\n * replies). Encoded as JSON into the `filter` query param.\n */\n filter?: {\n types?: string[]\n not_types?: string[]\n rel_types?: string[]\n not_rel_types?: string[]\n }\n }): Promise<{ chunk: Array<Record<string, unknown>>; end?: string }> {\n const params = new URLSearchParams({\n dir: 'b',\n limit: String(opts.limit ?? 50),\n user_id: opts.asUserId,\n })\n if (opts.from) params.set('from', opts.from)\n if (opts.filter) params.set('filter', JSON.stringify(opts.filter))\n const url =\n `${this.homeserver}/_matrix/client/v3/rooms/${encodeURIComponent(opts.roomId)}` +\n `/messages?${params.toString()}`\n const r = await this.fetch(url, {\n method: 'GET',\n headers: { Authorization: `Bearer ${this.asToken}` },\n })\n if (!r.ok) throw new Error(`fetchRoomMessages(${opts.roomId}) failed: ${r.status}`)\n return (await r.json()) as { chunk: Array<Record<string, unknown>>; end?: string }\n }\n\n async getJoinedMembers(\n roomId: string,\n asUserId: string,\n ): Promise<{ joined: Record<string, { display_name?: string }> }> {\n const url =\n `${this.homeserver}/_matrix/client/v3/rooms/${encodeURIComponent(roomId)}` +\n `/joined_members?user_id=${encodeURIComponent(asUserId)}`\n const r = await this.fetch(url, {\n method: 'GET',\n headers: { Authorization: `Bearer ${this.asToken}` },\n })\n if (!r.ok) throw new Error(`getJoinedMembers(${roomId}) failed: ${r.status}`)\n return (await r.json()) as { joined: Record<string, { display_name?: string }> }\n }\n\n async sync(opts: {\n asUserId: string\n since?: string | null\n timeoutMs?: number\n }): Promise<{\n next_batch: string\n rooms: {\n join: Record<string, {\n timeline: { events: Record<string, unknown>[]; prev_batch?: string; limited?: boolean }\n }>\n }\n }> {\n const params = new URLSearchParams({\n user_id: opts.asUserId,\n timeout: String(opts.timeoutMs ?? 30_000),\n })\n if (opts.since) params.set('since', opts.since)\n const url = `${this.homeserver}/_matrix/client/v3/sync?${params.toString()}`\n const r = await this.fetch(url, {\n headers: { Authorization: `Bearer ${this.asToken}` },\n })\n if (!r.ok) throw new Error(`sync(${opts.asUserId}) failed: ${r.status}`)\n return r.json() as Promise<{\n next_batch: string\n rooms: {\n join: Record<string, {\n timeline: { events: Record<string, unknown>[]; prev_batch?: string; limited?: boolean }\n }>\n }\n }>\n }\n\n async fetchRoomName(roomId: string, asUserId: string): Promise<string | null> {\n const url =\n `${this.homeserver}/_matrix/client/v3/rooms/${encodeURIComponent(roomId)}` +\n `/state/m.room.name/?user_id=${encodeURIComponent(asUserId)}`\n const r = await this.fetch(url, {\n method: 'GET',\n headers: { Authorization: `Bearer ${this.asToken}` },\n })\n if (r.status === 404) return null\n if (!r.ok) throw new Error(`fetchRoomName(${roomId}) failed: ${r.status}`)\n const body = (await r.json()) as { name?: string }\n return body.name ?? null\n }\n\n private async sendEvent(\n roomId: string,\n asUserId: string,\n eventType: string,\n content: Record<string, unknown>,\n ): Promise<{ event_id: string }> {\n const txn = randomUUID()\n const url =\n `${this.homeserver}/_matrix/client/v3/rooms/${encodeURIComponent(roomId)}` +\n `/send/${eventType}/${txn}?user_id=${encodeURIComponent(asUserId)}`\n const r = await this.fetch(url, {\n method: 'PUT',\n headers: { Authorization: `Bearer ${this.asToken}` },\n body: JSON.stringify(content),\n })\n if (!r.ok) throw new Error(`sendEvent(${eventType}) failed: ${r.status}`)\n return (await r.json()) as { event_id: string }\n }\n}\n","import type {\n TransportContextProvider,\n HistoryOptions,\n HistoryPage,\n Member,\n ChannelInfo,\n Message,\n ThreadOverview,\n ThreadOverviewPage,\n} from '@zooid/core'\nimport type { MatrixClient } from './matrix-client.js'\n\ninterface MatrixMessageEvent {\n event_id: string\n sender: string\n origin_server_ts: number\n type: string\n content?: {\n msgtype?: string\n body?: string\n 'm.relates_to'?: { rel_type?: string; event_id?: string }\n }\n unsigned?: {\n 'm.relations'?: {\n 'm.thread'?: {\n count?: number\n latest_event?: { origin_server_ts?: number }\n }\n }\n }\n}\n\nexport interface MatrixContextProviderOpts {\n client: MatrixClient\n /** AS sender_localpart user (read access). */\n asUserId: string\n /** Map of Matrix user IDs → agent names, for is_agent / agent_name flags. */\n agentBots: Map<string, string>\n}\n\nexport class MatrixContextProvider implements TransportContextProvider {\n constructor(private readonly opts: MatrixContextProviderOpts) {}\n\n async getRoomHistory(channelId: string, hopts: HistoryOptions): Promise<HistoryPage> {\n // Server-side filter: only `m.room.message` events. Without this we'd\n // burn the page budget on reactions, `dev.zooid.*` custom events, typing\n // notifications, etc., and routinely return empty pages with a stale\n // `has_more` cursor.\n const { chunk, end } = await this.opts.client.fetchRoomMessages({\n roomId: channelId,\n asUserId: this.opts.asUserId,\n limit: hopts.limit,\n from: hopts.before,\n filter: { types: ['m.room.message'] },\n })\n const messages: Message[] = []\n for (let i = chunk.length - 1; i >= 0; i--) {\n const ev = chunk[i] as unknown as MatrixMessageEvent\n const msg = this.toMessage(ev)\n if (msg) messages.push(msg)\n }\n return {\n messages,\n next_before: end,\n has_more: end !== undefined,\n }\n }\n\n async getRecentThreads(\n channelId: string,\n hopts: HistoryOptions,\n ): Promise<ThreadOverviewPage> {\n // Server-side filter: `m.room.message` only, and exclude thread replies\n // (`not_rel_types: ['m.thread']`) so the overview shows top-level entries\n // and thread roots, not the reply noise underneath them.\n const { chunk, end } = await this.opts.client.fetchRoomMessages({\n roomId: channelId,\n asUserId: this.opts.asUserId,\n limit: hopts.limit,\n from: hopts.before,\n filter: { types: ['m.room.message'], not_rel_types: ['m.thread'] },\n })\n // /messages returns newest-first; keep that order for the overview.\n const threads: ThreadOverview[] = []\n for (const ev of chunk as unknown as MatrixMessageEvent[]) {\n if (ev.type !== 'm.room.message') continue\n if (ev.content?.msgtype !== 'm.text' || typeof ev.content.body !== 'string') continue\n const relatesTo = ev.content['m.relates_to']\n if (relatesTo?.rel_type === 'm.thread') continue // skip thread replies\n const agent = this.opts.agentBots.get(ev.sender)\n const bundled = ev.unsigned?.['m.relations']?.['m.thread']\n const replyCount = bundled?.count ?? 0\n const latestTs = bundled?.latest_event?.origin_server_ts ?? ev.origin_server_ts\n threads.push({\n id: ev.event_id,\n sender: ev.sender,\n text: ev.content.body,\n timestamp: new Date(ev.origin_server_ts).toISOString(),\n is_agent: agent !== undefined,\n ...(agent !== undefined ? { agent_name: agent } : {}),\n reply_count: replyCount,\n last_activity_at: new Date(latestTs).toISOString(),\n })\n }\n return {\n threads,\n next_before: end,\n has_more: end !== undefined,\n }\n }\n\n async getThreadHistory(\n channelId: string,\n threadId: string,\n hopts: HistoryOptions,\n ): Promise<HistoryPage> {\n // Root event first (only on the first page when no pagination cursor).\n const messages: Message[] = []\n if (!hopts.before) {\n const root = (await this.opts.client.fetchEvent(\n channelId,\n threadId,\n this.opts.asUserId,\n )) as unknown as MatrixMessageEvent | null\n if (root) {\n const rootMsg = this.toMessage(root)\n if (rootMsg) messages.push({ ...rootMsg, thread_id: threadId })\n }\n }\n const { chunk, next_batch } = await this.opts.client.fetchThreadRelations({\n roomId: channelId,\n rootEventId: threadId,\n asUserId: this.opts.asUserId,\n limit: hopts.limit,\n from: hopts.before,\n })\n for (const ev of chunk as unknown as MatrixMessageEvent[]) {\n const reply = this.toMessage(ev)\n if (reply) messages.push({ ...reply, thread_id: threadId })\n }\n return {\n messages,\n next_before: next_batch,\n has_more: next_batch !== undefined,\n }\n }\n\n private toMessage(ev: MatrixMessageEvent): Message | null {\n if (ev.type !== 'm.room.message') return null\n const msgtype = ev.content?.msgtype\n const body = ev.content?.body\n const agent = this.opts.agentBots.get(ev.sender)\n const relatesTo = ev.content?.['m.relates_to']\n const threadId =\n relatesTo?.rel_type === 'm.thread' && relatesTo.event_id ? relatesTo.event_id : undefined\n\n // Media events render as context placeholders\n if (msgtype === 'm.image' || msgtype === 'm.file' || msgtype === 'm.video' || msgtype === 'm.audio') {\n const kind = msgtype.slice(2) // 'image', 'file', 'video', 'audio'\n const name = typeof body === 'string' && body ? body : 'untitled'\n return {\n id: ev.event_id,\n sender: ev.sender,\n text: `[${kind}: ${name}]`,\n timestamp: new Date(ev.origin_server_ts).toISOString(),\n is_agent: agent !== undefined,\n ...(agent !== undefined ? { agent_name: agent } : {}),\n ...(threadId !== undefined ? { thread_id: threadId } : {}),\n }\n }\n\n if (msgtype !== 'm.text' || typeof body !== 'string') return null\n return {\n id: ev.event_id,\n sender: ev.sender,\n text: body,\n timestamp: new Date(ev.origin_server_ts).toISOString(),\n is_agent: agent !== undefined,\n ...(agent !== undefined ? { agent_name: agent } : {}),\n ...(threadId !== undefined ? { thread_id: threadId } : {}),\n }\n }\n\n async getChannelMembers(channelId: string): Promise<Member[]> {\n const { joined } = await this.opts.client.getJoinedMembers(channelId, this.opts.asUserId)\n return Object.entries(joined).map(([id, info]) => {\n const agent = this.opts.agentBots.get(id)\n return {\n id,\n name: info.display_name ?? id,\n is_agent: agent !== undefined,\n ...(agent !== undefined ? { agent_name: agent } : {}),\n }\n })\n }\n\n async getChannelInfo(channelId: string): Promise<ChannelInfo> {\n const name = await this.opts.client.fetchRoomName(channelId, this.opts.asUserId)\n return {\n id: channelId,\n name: name ?? channelId,\n transport: 'matrix',\n }\n }\n}\n","import { stringify } from 'yaml'\n\nexport interface MatrixTransportConfig {\n id: string\n url: string\n homeserver: string\n asToken: string\n hsToken: string\n senderLocalpart: string\n /** Regex covering all bot users, e.g. `@.*:example.com` */\n userNamespace: string\n /**\n * Optional regex covering aliases the AS may claim, e.g. `#.*:example.com`.\n * Required when the AS calls `createRoom` with a `room_alias_name`.\n */\n aliasNamespace?: string\n /**\n * Whether the AS exclusively owns the user_namespace. Default true.\n * Set false when humans share the namespace (e.g., `zooid dev` registers\n * a predefined admin under the same `@.*:localhost` regex).\n */\n exclusive?: boolean\n}\n\nexport function renderRegistration(c: MatrixTransportConfig): string {\n return stringify(\n {\n id: c.id,\n url: c.url,\n as_token: c.asToken,\n hs_token: c.hsToken,\n sender_localpart: c.senderLocalpart,\n rate_limited: false,\n namespaces: {\n users: [{ exclusive: c.exclusive ?? true, regex: c.userNamespace }],\n aliases: c.aliasNamespace\n ? [{ exclusive: c.exclusive ?? true, regex: c.aliasNamespace }]\n : [],\n rooms: [],\n },\n },\n { defaultStringType: 'PLAIN', defaultKeyType: 'PLAIN', singleQuote: true },\n )\n}\n","// A workstation id (and an agent key) must be \"slug-shaped\": kebab-case,\n// MXID/alias-safe. SLUG_RE names the *format*; the domain noun is workstation.\nexport const SLUG_RE = /^[a-z0-9-]+$/\nexport const AGENT_KEY_RE = /^[a-z0-9-]+$/\n\nexport const isValidWorkstation = (s: string): boolean => SLUG_RE.test(s)\nexport const isValidAgentKey = (s: string): boolean => AGENT_KEY_RE.test(s)\n\nexport function agentMxid(workstation: string, agent: string, serverName: string): string {\n if (!isValidWorkstation(workstation))\n throw new Error(`invalid workstation: ${JSON.stringify(workstation)}`)\n if (!isValidAgentKey(agent)) throw new Error(`invalid agent key: ${JSON.stringify(agent)}`)\n return `@${workstation}.${agent}:${serverName}`\n}\n\nexport function splitAgentLocalpart(localpart: string): { workstation: string; agent: string } {\n const i = localpart.indexOf('.') // first dot is the boundary; workstation/agent are dot-free\n if (i <= 0 || i === localpart.length - 1)\n throw new Error(`not a workstation.agent localpart: ${localpart}`)\n return { workstation: localpart.slice(0, i), agent: localpart.slice(i + 1) }\n}\n\nexport function workstationUserNamespace(workstation: string, serverName: string): string {\n if (!isValidWorkstation(workstation))\n throw new Error(`invalid workstation: ${JSON.stringify(workstation)}`)\n return `@${workstation}\\\\..*:${serverName}` // escaped dot — exclusive to this workstation's agents\n}\n","export interface MaybeMessage {\n content?: {\n 'm.mentions'?: { user_ids?: string[] }\n body?: string\n formatted_body?: string\n }\n}\n\nconst MATRIX_TO_RE = /https:\\/\\/matrix\\.to\\/#\\/(@[^\"<>\\s]+)/g\nconst RAW_USER_RE = /(@[A-Za-z0-9._\\-=/+]+:[A-Za-z0-9.\\-]+)/g\n\nexport function extractMentions(event: MaybeMessage): string[] {\n const out = new Set<string>()\n const c = event.content ?? {}\n for (const id of c['m.mentions']?.user_ids ?? []) out.add(id)\n if (c.formatted_body) {\n for (const m of c.formatted_body.matchAll(MATRIX_TO_RE)) {\n out.add(decodeURIComponent(m[1]))\n }\n }\n if (c.body && out.size === 0) {\n for (const m of c.body.matchAll(RAW_USER_RE)) out.add(m[1])\n }\n return [...out]\n}\n\n/**\n * Remove a single user-id mention from a message body. Strips the raw\n * `@local:server` form and collapses any whitespace that the strip leaves\n * behind. Other users' mentions are preserved verbatim so the agent can\n * reason about them.\n */\nexport function stripMention(body: string, userId: string): string {\n const escaped = userId.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')\n // Match optional surrounding whitespace so we don't leave double spaces.\n const re = new RegExp(`\\\\s*${escaped}\\\\s*`, 'g')\n return body.replace(re, ' ').replace(/\\s+/g, ' ').trim()\n}\n","import type { RoomBinding } from '@zooid/core'\nimport { extractMentions } from './mentions.js'\n\nexport type { RoomBinding }\n\nexport interface AgentBinding {\n name: string\n userId: string\n /** Optional human-readable display name. Falls back to the user_id localpart. */\n displayName?: string\n /**\n * Rooms this agent is bound to. Each entry's `alias` starts out as the\n * configured `#alias` (or `!id`) and is rewritten to the canonical room\n * ID by `BotPool.bootstrap`. Optional `powerLevel` is seeded into the\n * room's `m.room.power_levels.users` at room creation only.\n */\n rooms: RoomBinding[]\n trigger: 'mention' | 'any'\n /** Host path of the agent's workspace (resolved agent.workdir). Media files land here. */\n workspaceDir?: string\n /** Path prefix as the agent sees it: '/workspace' for containers, = workspaceDir for local. */\n agentWorkspacePath?: string\n}\n\nexport const MEDIA_MSGTYPES = new Set(['m.image', 'm.file', 'm.video', 'm.audio'])\n\nexport function isMediaMsgtype(t: string | undefined): boolean {\n return t !== undefined && MEDIA_MSGTYPES.has(t)\n}\n\nexport interface ThreadState {\n /** Agent names that have posted in this thread, in order. */\n participants: string[]\n /** Agent names @mentioned in the thread root event (or subsequently). */\n rootMentions: string[]\n /**\n * Agent-to-agent call edges: sub-agent name → the agent that @mentioned\n * (called) it in this thread. A sub's bare reply bubbles up to its caller;\n * a caller never implicitly re-triggers its callee. Makes agent↔agent\n * acknowledgement loops structurally impossible. See [[ZOD039]] §\n * Implicit triggers → Directional continuation.\n */\n callers: Record<string, string>\n /**\n * Handoff arcs per sub-agent: the event ids of the agent→agent @mention\n * messages that called it, in timeline order (last = current arc). Each\n * call opens a fresh ACP session keyed `threadRoot|callEventId`\n * ([[ZOD071]]). Append-only; rebuilt from the timeline after a restart.\n */\n handoffs: Record<string, string[]>\n}\n\ninterface MaybeEvent {\n type?: string\n room_id?: string\n sender?: string\n content?: {\n msgtype?: string\n 'm.relates_to'?: { rel_type?: string; event_id?: string }\n }\n}\n\nexport type RouteMatch = AgentBinding\n\nfunction inboundThreadRoot(event: MaybeEvent): string | undefined {\n const r = event.content?.['m.relates_to']\n return r?.rel_type === 'm.thread' && r.event_id ? r.event_id : undefined\n}\n\nexport function route(\n event: MaybeEvent,\n agents: AgentBinding[],\n threadStates?: Map<string, ThreadState>,\n): RouteMatch[] {\n if (event.type !== 'm.room.message') return []\n if (!event.content?.msgtype) return []\n if (isMediaMsgtype(event.content.msgtype)) return []\n const mentions = new Set(extractMentions(event as never))\n const matches: RouteMatch[] = []\n const threadRoot = inboundThreadRoot(event)\n const threadState = threadRoot ? threadStates?.get(threadRoot) : undefined\n\n for (const a of agents) {\n if (event.sender === a.userId) continue\n if (!a.rooms.some((r) => r.alias === event.room_id)) continue\n if (a.trigger === 'any') {\n matches.push(a)\n continue\n }\n // trigger === 'mention'\n if (mentions.has(a.userId)) {\n matches.push(a)\n continue\n }\n // Implicit trigger in a thread.\n if (threadState) {\n const senderAgent = agents.find((x) => x.userId === event.sender)\n if (senderAgent) {\n // Agent reply = a \"return\": route only to the agent that called the\n // sender (its caller), never to a callee. Directional continuation\n // keeps agent↔agent handoffs from looping — the call graph is a tree\n // rooted at the human, so returns only ever walk up.\n if (threadState.callers[senderAgent.name] === a.name) matches.push(a)\n } else {\n // Human (or non-agent) follow-up: continue with the most-recent-posting\n // agent, or inherit the root mention if no agent has posted yet.\n const lastPoster = threadState.participants.at(-1)\n if (lastPoster) {\n if (lastPoster === a.name) matches.push(a)\n } else if (threadState.rootMentions.includes(a.name)) {\n matches.push(a)\n }\n }\n }\n }\n return matches\n}\n","import { MatrixClient } from './matrix-client.js'\n\nexport interface EnsureSpaceOpts {\n client: MatrixClient\n asUserId: string\n serverName: string\n spaceLocalpart: string\n preset: 'public_chat' | 'private_chat'\n /**\n * Operator MXIDs to seed at PL 100 in the space's `m.room.power_levels`\n * at creation. The AS bot is always included. Empty/absent → no override\n * (the preset's PL defaults apply). Only consulted on first creation —\n * if the alias already resolves we return the existing room untouched.\n */\n admins?: string[]\n /**\n * Join rule pinned on the space at creation. Defaults to `invite`: a\n * workspace is joined by invitation, not self-service, so it can't be walked\n * into (which would otherwise satisfy every restricted child room's `allow`).\n * `zooid dev` passes `public` so a self-service-registered local account can\n * join `#<space>` straight from the web client without an invite — acceptable\n * because the dev homeserver is local-only and never deployed.\n */\n joinRule?: 'invite' | 'public'\n}\n\nexport async function ensureWorkforceSpace(opts: EnsureSpaceOpts): Promise<string> {\n const alias = `#${opts.spaceLocalpart}:${opts.serverName}`\n const existing = await opts.client.resolveAlias(alias)\n if (existing) return existing\n\n const display = opts.spaceLocalpart.charAt(0).toUpperCase() + opts.spaceLocalpart.slice(1)\n const body: Record<string, unknown> = {\n room_alias_name: opts.spaceLocalpart,\n name: display,\n preset: opts.preset,\n creation_content: { type: 'm.space' },\n // Pin the join rule regardless of preset. Defaults to invite so the space\n // can't be walked into (which would otherwise satisfy every restricted\n // child room's allow); `zooid dev` opts into `public` for local-only use.\n initial_state: [\n { type: 'm.room.join_rules', state_key: '', content: { join_rule: opts.joinRule ?? 'invite' } },\n ],\n }\n if (opts.admins && opts.admins.length > 0) {\n // Invite each admin so they actually become members — PL 100 alone does\n // not grant membership in an invite-only space.\n body.invite = opts.admins\n const users: Record<string, number> = { [opts.asUserId]: 100 }\n for (const a of opts.admins) users[a] = 100\n body.power_level_content_override = { users }\n }\n return opts.client.createRoomRaw({ asUserId: opts.asUserId, body })\n}\n\nexport interface EnsureDefaultChannelOpts {\n client: MatrixClient\n asUserId: string\n serverName: string\n spaceId: string\n /** Localpart of the default channel; defaults to `general`. */\n channelLocalpart?: string\n /**\n * Operator MXIDs to seed at PL 100 in the channel's `m.room.power_levels`\n * at creation. The AS bot is always included. Empty/absent → no override\n * (the preset's PL defaults apply). Only consulted on first creation —\n * if the alias already resolves we return the existing room untouched.\n */\n admins?: string[]\n}\n\n/**\n * Ensure a space has a default channel (`#general` by default), restricted to\n * the space's members and attached as an `m.space.child`. Idempotent: returns\n * the existing room if the alias already resolves. Has no agent — it's the\n * human landing room, so it is created here at provisioning time rather than\n * via the agent-room path.\n */\nexport async function ensureDefaultChannel(opts: EnsureDefaultChannelOpts): Promise<string> {\n const localpart = opts.channelLocalpart ?? 'general'\n const alias = `#${localpart}:${opts.serverName}`\n const existing = await opts.client.resolveAlias(alias)\n if (existing) return existing\n\n let userPowerLevels: Record<string, number> | undefined\n if (opts.admins && opts.admins.length > 0) {\n userPowerLevels = { [opts.asUserId]: 100 }\n for (const a of opts.admins) userPowerLevels[a] = 100\n }\n\n const roomId = await opts.client.createRoom({\n roomAliasName: localpart,\n invite: [],\n senderUserId: opts.asUserId,\n name: localpart.charAt(0).toUpperCase() + localpart.slice(1),\n restrictedToSpaceId: opts.spaceId,\n ...(userPowerLevels ? { userPowerLevels } : {}),\n })\n await opts.client.sendStateEvent({\n roomId: opts.spaceId,\n asUserId: opts.asUserId,\n eventType: 'm.space.child',\n stateKey: roomId,\n content: { via: [opts.serverName] },\n })\n return roomId\n}\n\nexport function serverNameFromMxid(mxid: string): string {\n const colon = mxid.indexOf(':')\n if (colon < 0) {\n throw new Error(`mxid lacks server: ${mxid}`)\n }\n return mxid.slice(colon + 1)\n}\n","import type { MatrixClient } from './matrix-client.js'\nimport type { AgentBinding } from './router.js'\nimport { serverNameFromMxid } from './space-provisioner.js'\n\nexport interface BootstrapOpts {\n /** Invited to any newly-created room; absent = no invite. */\n adminUserId?: string\n /** Workforce space room ID. When set, every resolved agent room is attached as m.space.child. */\n spaceRoomId?: string\n /** AS bot user ID. Required when spaceRoomId is set; sender of the m.space.child write. */\n asUserId?: string\n /**\n * Operator MXIDs seeded at PL 100 in every agent room this pool creates.\n * Applied via `power_level_content_override.users` at room creation only —\n * never reconciled. Empty/absent = no operator entries.\n */\n adminUserIds?: string[]\n}\n\nexport class BotPool {\n constructor(\n private readonly client: Pick<\n MatrixClient,\n | 'registerBot'\n | 'invite'\n | 'joinRoom'\n | 'resolveAlias'\n | 'createRoom'\n | 'sendStateEvent'\n | 'setDisplayName'\n >,\n private readonly agents: AgentBinding[],\n ) {}\n\n async bootstrap(opts: BootstrapOpts = {}): Promise<void> {\n const aliasToId = new Map<string, string>()\n const attachedToSpace = new Set<string>()\n for (const a of this.agents) {\n const lp = localpart(a.userId)\n try {\n await this.client.registerBot(lp)\n } catch (err) {\n console.warn(`[matrix] register failed for ${a.userId}: ${(err as Error).message}`)\n }\n try {\n await this.client.setDisplayName(a.userId, a.displayName ?? lp)\n } catch (err) {\n console.warn(`[matrix] setDisplayName(${a.userId}) failed: ${(err as Error).message}`)\n }\n // Make each agent a member of the workforce space. That covers two\n // things at once: the dev.zooid.workforce roster is now backed by\n // actual space membership (so the Zoon client's member autocomplete\n // works across rooms), and every restricted child room's allow rule\n // is satisfied without per-room invites.\n if (opts.spaceRoomId && opts.asUserId) {\n try {\n await this.client.invite({\n roomId: opts.spaceRoomId,\n asUserId: opts.asUserId,\n targetUserId: a.userId,\n })\n await this.client.joinRoom(opts.spaceRoomId, a.userId)\n } catch (err) {\n console.warn(\n `[matrix] space membership for ${a.userId} failed: ${(err as Error).message}`,\n )\n }\n }\n for (let i = 0; i < a.rooms.length; i++) {\n const binding = a.rooms[i]!\n const room = binding.alias\n try {\n let resolved = room\n if (room.startsWith('#')) {\n const cached = aliasToId.get(room)\n if (cached) {\n resolved = cached\n } else {\n const existing = await this.client.resolveAlias(room)\n if (existing) {\n resolved = existing\n } else {\n const colon = room.indexOf(':')\n const aliasLocalpart = colon > 1 ? room.slice(1, colon) : room.slice(1)\n // Workstation-created rooms are owned by the AS sender bot (PL\n // 100, already seeded in the power override below). Agents join\n // as plain members and must never be room admins. The human\n // operator is invited + seeded at PL 100, but can't be the\n // creator under a workstation-scoped namespace — the AS token\n // can't impersonate a non-namespaced human.\n const sender = opts.asUserId ?? opts.adminUserId ?? a.userId\n const userPowerLevels = buildUserPowerLevels(\n opts.asUserId,\n opts.adminUserIds,\n this.agents,\n room,\n )\n try {\n resolved = await this.client.createRoom({\n roomAliasName: aliasLocalpart,\n invite: opts.adminUserId ? [opts.adminUserId] : [],\n senderUserId: sender,\n name: aliasLocalpart,\n ...(opts.spaceRoomId ? { restrictedToSpaceId: opts.spaceRoomId } : {}),\n ...(userPowerLevels ? { userPowerLevels } : {}),\n })\n } catch (err) {\n // Another agent — or another daemon process bootstrapping the\n // same workforce — may have created this room concurrently, so\n // its alias is now taken (createRoom fails with the alias in\n // use). Re-resolve and join the existing room rather than\n // leaving an orphan. Only rethrow if the alias still doesn't\n // resolve (a genuine creation failure).\n const raced = await this.client.resolveAlias(room)\n if (!raced) throw err\n resolved = raced\n }\n }\n aliasToId.set(room, resolved)\n }\n }\n // Rewrite the binding's alias to the canonical room ID so the\n // router (which matches on event.room_id) sees a hit when Tuwunel\n // pushes events. The declared powerLevel was a creation-time\n // seed — it has no role after bootstrap.\n binding.alias = resolved\n await this.client.joinRoom(resolved, a.userId)\n\n if (\n opts.spaceRoomId &&\n opts.asUserId &&\n !attachedToSpace.has(resolved)\n ) {\n attachedToSpace.add(resolved)\n const via = serverNameFromMxid(a.userId)\n try {\n await this.client.sendStateEvent({\n roomId: opts.spaceRoomId,\n asUserId: opts.asUserId,\n eventType: 'm.space.child',\n stateKey: resolved,\n content: { via: [via] },\n })\n } catch (err) {\n console.warn(\n `[matrix] m.space.child(${resolved}) failed: ${(err as Error).message}`,\n )\n }\n }\n } catch (err) {\n console.warn(\n `[matrix] join failed (${a.userId} → ${room}): ${(err as Error).message}`,\n )\n }\n }\n }\n }\n\n findByUserId(userId: string): AgentBinding | undefined {\n return this.agents.find((a) => a.userId === userId)\n }\n\n findByName(name: string): AgentBinding | undefined {\n return this.agents.find((a) => a.name === name)\n }\n}\n\nfunction localpart(userId: string): string {\n const m = /^@([^:]+):/.exec(userId)\n if (!m) throw new Error(`bad user id: ${userId}`)\n return m[1]\n}\n\n/**\n * Build the `userPowerLevels` map for a room about to be created. Always\n * seeds the AS bot (when known) and any operator admins at PL 100; layers\n * per-agent declared PLs on top so agents land at moderator (or whatever\n * they declared) without a follow-up state write. Returns undefined when\n * the map would be empty — `createRoom` then omits the override entirely\n * and the preset's defaults apply.\n */\nfunction buildUserPowerLevels(\n asUserId: string | undefined,\n admins: string[] | undefined,\n agents: AgentBinding[],\n roomAlias: string,\n): Record<string, number> | undefined {\n const users: Record<string, number> = {}\n if (asUserId) users[asUserId] = 100\n if (admins) for (const a of admins) users[a] = 100\n for (const a of agents) {\n for (const r of a.rooms) {\n if (r.alias !== roomAlias) continue\n if (r.powerLevel !== undefined) users[a.userId] = r.powerLevel\n }\n }\n return Object.keys(users).length > 0 ? users : undefined\n}\n","import { Hono } from 'hono'\nimport { timingSafeEqual } from 'node:crypto'\nimport type { AcpRegistry, ApprovalCorrelator, RegisteredApproval } from '@zooid/core'\nimport type { AgentEvent, ContentBlock } from '@zooid/acp-client'\nimport { MatrixClient } from './matrix-client.js'\nimport { BotPool } from './bot-pool.js'\nimport { route, isMediaMsgtype, type AgentBinding, type ThreadState } from './router.js'\nimport { sessionKeyFor, composeHandoffKey } from './session-keys.js'\nimport { stripMention, extractMentions } from './mentions.js'\nimport { toToolCallBody, toUpdateBody, toPlanBody, toAvailableCommandsBody, toErrorBody } from './event-encoders.js'\nimport { classify } from '@zooid/acp-client'\nimport { toMatrixHtml } from './markdown-to-matrix-html.js'\nimport {\n PendingMediaStore,\n type PendingMediaItem,\n} from './pending-media.js'\nimport {\n MediaClient,\n MAX_INLINE_IMAGE_BYTES,\n INLINE_IMAGE_MIMES,\n} from './media-client.js'\nimport { writeAttachment } from './attachments.js'\nimport { SyncLoop } from './sync-loop.js'\n\nexport interface MediaClientLike {\n download(input: {\n mxcUri: string\n asUserId: string\n maxBytes?: number\n }): Promise<{ data: Uint8Array; contentType: string }>\n upload(input: {\n data: Uint8Array\n contentType: string\n filename?: string\n asUserId: string\n }): Promise<{ content_uri: string }>\n}\n\nexport interface CreateMatrixTransportOptions {\n agents: AcpRegistry\n approvals: ApprovalCorrelator\n client: MatrixClient\n bindings: AgentBinding[]\n hsToken: string\n /** Admin Matrix user ID. When set, BotPool.bootstrap invites this user into rooms it creates. */\n adminUserId?: string\n /** Post-turn drain: keep collecting trailing `agent_message_chunk`s until the\n * buffer is quiet for this long before flushing. Defaults to `DRAIN_QUIET_MS`.\n * Set to 0 to disable the drain (e.g. in tests). */\n drainQuietMs?: number\n /** Hard cap on the post-turn drain. Defaults to `DRAIN_MAX_MS`. */\n drainMaxMs?: number\n /** Injected media client for downloading/uploading Matrix media. */\n media?: MediaClientLike\n /** Injected attachment writer (defaults to the real writeAttachment). */\n writeAttachmentFn?: typeof writeAttachment\n /** AS sender-bot MXID (@<sender_localpart>:<server>). Together with the agent\n * bindings this forms the set of \"our bot users\" whose ad-hoc invites are\n * declined. */\n botUserId?: string\n /**\n * Transport ingestion mode.\n * - `'appservice'` (default): Tuwunel pushes events to the HTTP transaction endpoint.\n * - `'client'`: daemon polls via impersonated `/sync` per agent (pull mode).\n */\n mode?: 'appservice' | 'client'\n /** Pull mode: load the persisted `since` cursor for an agent user ID. */\n loadSince?: (agentUserId: string) => string | null\n /** Pull mode: persist the `since` cursor after each sync poll. */\n saveSince?: (agentUserId: string, since: string) => void\n}\n\ninterface SessionContext {\n agent: AgentBinding\n roomId: string\n /** Always set — every session is thread-scoped via agent-promotion. */\n threadRoot: string\n}\n\ninterface MatrixEvent {\n type?: string\n event_id?: string\n origin_server_ts?: number\n room_id?: string\n sender?: string\n /** Present on state events (m.room.member → the affected user). */\n state_key?: string\n content?: Record<string, unknown> & {\n msgtype?: string\n body?: string\n membership?: string\n 'm.relates_to'?: { rel_type?: string; event_id?: string }\n }\n}\n\nconst STARTUP_GRACE_MS = 5_000\n\ninterface MediaBlocksResult {\n blocks: ContentBlock[]\n pathLines: string[]\n}\n\nasync function buildMediaBlocks(\n items: PendingMediaItem[],\n opts: {\n agent: AgentBinding\n media: MediaClientLike | undefined\n writeAttachmentFn: typeof writeAttachment\n onError: (item: PendingMediaItem, err: unknown) => void\n },\n): Promise<MediaBlocksResult> {\n const blocks: ContentBlock[] = []\n const pathLines: string[] = []\n\n if (!opts.media || items.length === 0) return { blocks, pathLines }\n\n for (const item of items) {\n try {\n const isInlineCandidate =\n item.msgtype === 'm.image' &&\n INLINE_IMAGE_MIMES.includes(item.info?.mimetype ?? '') &&\n (item.info?.size === undefined || item.info.size <= MAX_INLINE_IMAGE_BYTES)\n\n if (isInlineCandidate) {\n const { data, contentType } = await opts.media.download({\n mxcUri: item.url,\n asUserId: opts.agent.userId,\n })\n // Double-check actual size (info can lie)\n if (data.byteLength <= MAX_INLINE_IMAGE_BYTES) {\n blocks.push({\n type: 'image',\n data: Buffer.from(data).toString('base64'),\n mimeType: contentType,\n })\n continue\n }\n // Actual size exceeded cap — fall through to file route with the already-downloaded bytes\n if (opts.agent.workspaceDir) {\n const paths = opts.writeAttachmentFn({\n workspaceDir: opts.agent.workspaceDir,\n agentWorkspacePath: opts.agent.agentWorkspacePath ?? opts.agent.workspaceDir,\n eventId: item.eventId,\n filename: item.filename ?? item.body,\n data,\n })\n blocks.push({\n type: 'resource_link',\n uri: `file://${paths.agentPath}`,\n name: item.filename ?? item.body,\n })\n pathLines.push(`Attached file: ${paths.agentPath}`)\n }\n } else {\n // File route (m.file, m.video, m.audio, or oversized image)\n if (!opts.agent.workspaceDir) continue\n const { data } = await opts.media.download({\n mxcUri: item.url,\n asUserId: opts.agent.userId,\n })\n const paths = opts.writeAttachmentFn({\n workspaceDir: opts.agent.workspaceDir,\n agentWorkspacePath: opts.agent.agentWorkspacePath ?? opts.agent.workspaceDir,\n eventId: item.eventId,\n filename: item.filename ?? item.body,\n data,\n })\n blocks.push({\n type: 'resource_link',\n uri: `file://${paths.agentPath}`,\n name: item.filename ?? item.body,\n mimeType: item.info?.mimetype,\n size: item.info?.size,\n })\n pathLines.push(`Attached file: ${paths.agentPath}`)\n }\n } catch (err) {\n opts.onError(item, err)\n }\n }\n\n return { blocks, pathLines }\n}\n\nasync function sendMediaError(\n ctx: { agent: AgentBinding; roomId: string; threadRoot: string },\n _err: unknown,\n message: string,\n client: MatrixClient,\n): Promise<void> {\n await client\n .sendCustomEvent({\n roomId: ctx.roomId,\n asUserId: ctx.agent.userId,\n eventType: 'dev.zooid.error',\n content: toErrorBody(\n {\n kind: 'error' as const,\n agentId: ctx.agent.name,\n sessionId: null,\n turnId: null,\n code: 'media_failed',\n message: message.slice(0, 250),\n transient: false,\n },\n ctx.threadRoot,\n ),\n })\n .catch((e) => console.warn(`[matrix:${ctx.agent.name}] dev.zooid.error send failed:`, e))\n}\nconst SEEN_EVENT_CAP = 5_000\n\n// ACP only guarantees that an agent flushes pending `session/update`\n// notifications before the `session/prompt` response in the *cancellation*\n// path; for a normal turn the ordering is unspecified. Some agents (e.g.\n// opencode) emit trailing `agent_message_chunk`s a few ms after the stopReason\n// response, so finalizing the moment `prompt()` resolves truncates the reply.\n// After the turn resolves we wait for the buffer to stay unchanged for\n// DRAIN_QUIET_MS (debounce — re-arms on each late chunk) before flushing,\n// capped at DRAIN_MAX_MS so a misbehaving stream can't hang the turn.\nconst DRAIN_QUIET_MS = 300\n// 30s upper bound on how long we wait after `session/prompt` resolves before\n// flushing whatever we have (or declaring an empty turn). Set high because\n// some agents — opencode especially — resolve the prompt promise *before*\n// the agent_message_chunk stream starts, and the chunk burst can be 5–15s\n// after that. The drain still short-circuits via DRAIN_QUIET_MS once any\n// content has settled, so this cap only kicks in for genuinely-stuck turns.\nconst DRAIN_MAX_MS = 30_000\n\nconst delay = (ms: number): Promise<void> => new Promise((r) => setTimeout(r, ms))\n\nfunction inboundThreadRoot(evt: MatrixEvent): string | undefined {\n const r = evt.content?.['m.relates_to']\n return r?.rel_type === 'm.thread' && r.event_id ? r.event_id : undefined\n}\n\nexport function createMatrixTransport(opts: CreateMatrixTransportOptions) {\n const { agents, approvals, client, bindings, hsToken, adminUserId, botUserId, mode = 'appservice' } = opts\n const drainQuietMs = opts.drainQuietMs ?? DRAIN_QUIET_MS\n const drainMaxMs = opts.drainMaxMs ?? DRAIN_MAX_MS\n const mediaClient = opts.media\n const writeAttachmentFn = opts.writeAttachmentFn ?? writeAttachment\n const pendingMedia = new PendingMediaStore()\n const pool = new BotPool(client, bindings)\n const ourBotUserIds = new Set<string>([\n ...(botUserId ? [botUserId] : []),\n ...bindings.map((b) => b.userId),\n ])\n const DECLINE_REASON =\n 'Bots are placed in rooms only by the zooid daemon (workforce-as-code). ' +\n 'Ad-hoc invites are declined — add the bot to the room in zooid.yaml.'\n const sessions = new Map<string, SessionContext>()\n const buffers = new Map<string, string>()\n // Last messageId seen per session's buffer. opencode streams each assistant\n // message under its own id with no delimiter chunk between them, so a change\n // here marks a message boundary we must break on.\n const bufferMessageIds = new Map<string, string>()\n // Per-session promise tail so out-of-band events (tool_call, plan, etc.)\n // serialize on the wire even though the ACP producer doesn't await us.\n const sendQueue = new Map<string, Promise<void>>()\n // Thread participation index: keyed by thread root event_id.\n const threadStates = new Map<string, ThreadState>()\n // Drop events older than this — in push (appservice) mode Tuwunel may replay\n // a backlog after the daemon was offline, and we don't want yesterday's\n // \"@docs hi\" to fire now. In pull (client) mode the persisted `since` cursor\n // is the authoritative replay boundary (process everything after it — that's\n // exactly the offline-resume feature), so the timestamp guard must NOT apply:\n // the missed-while-offline mention is older than startup by design.\n const cutoffTs = mode === 'client' ? Number.NEGATIVE_INFINITY : Date.now() - STARTUP_GRACE_MS\n // Idempotency: appservice transactions are retried on 4xx/5xx/timeout, and\n // the same event_id can arrive twice. Skip ones we've already taken.\n const seenEventIds = new Set<string>()\n // Messages flushed per session this turn. Lets the drain loop tell \"stream\n // not started yet\" (0 flushes, empty buffer → keep waiting) from \"turn done,\n // last message already flushed mid-stream\" (>0 flushes, empty buffer → stop).\n const flushedCounts = new Map<string, number>()\n // Commands a shim advertises during session load/new — i.e. before runTurn\n // registers the session ctx (sessions.set). Stashed here keyed by sessionId\n // and replayed once the ctx exists, so `available_commands_update` (which is\n // only ever emitted at session establishment, never mid-turn) isn't dropped.\n const pendingCommands = new Map<string, AgentEvent>()\n\n // Build the m.text content for a chunk of assistant prose, attaching a\n // formatted_body only when the HTML render adds rich text the plain body\n // can't carry (marked wraps plain prose in <p>…</p>; skip that — most\n // clients render `body` better than a stripped re-encode).\n const buildTextContent = (\n text: string,\n ): { msgtype: string; body: string; [k: string]: unknown } => {\n const content: { msgtype: string; body: string; [k: string]: unknown } = {\n msgtype: 'm.text',\n body: text,\n }\n const html = toMatrixHtml(text)\n if (html) {\n const escapedPlain =\n '<p>' +\n text.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;') +\n '</p>'\n const norm = (s: string) => s.replace(/\\s+/g, ' ').trim()\n if (norm(html) !== norm(escapedPlain)) {\n content.format = 'org.matrix.custom.html'\n content.formatted_body = html\n }\n }\n return content\n }\n\n // Flush a session's buffered assistant text as its own Matrix message and\n // clear the buffer. No-op on an empty buffer. The send is chained onto\n // sendQueue so it orders correctly against tool_call/plan events from the\n // same turn. The buffer is cleared synchronously (before the first await),\n // so a chunk for the *next* message that arrives during the send starts\n // fresh. Returns true when a message was enqueued.\n const flushBuffer = (sessionId: string): boolean => {\n const ctx = sessions.get(sessionId)\n const text = buffers.get(sessionId) ?? ''\n if (!ctx || text.length === 0) return false\n buffers.set(sessionId, '')\n flushedCounts.set(sessionId, (flushedCounts.get(sessionId) ?? 0) + 1)\n const content = buildTextContent(text)\n const tail = (sendQueue.get(sessionId) ?? Promise.resolve()).then(async () => {\n try {\n await client.sendMessage({\n roomId: ctx.roomId,\n asUserId: ctx.agent.userId,\n content,\n threadRoot: ctx.threadRoot,\n })\n } catch (err) {\n console.warn(`[matrix:${ctx.agent.name}] sendMessage flush failed:`, err)\n }\n })\n sendQueue.set(sessionId, tail)\n return true\n }\n\n agents.onEvent = async (name, event: AgentEvent) => {\n const ctx = sessions.get(event.sessionId)\n if (!ctx) {\n // available_commands_update is advertised during ensureSession (session\n // load/new), before runTurn calls sessions.set — so the ctx isn't there\n // yet. Stash the latest roster and replay it once runTurn registers the\n // ctx. Other event types arriving without a ctx are genuinely orphaned\n // (e.g. replayed history for a thread we're not handling) — drop them.\n if (event.type === 'available_commands') {\n pendingCommands.set(event.sessionId, event)\n } else {\n console.warn(`[matrix:${name}] no session ctx for ${event.sessionId}`)\n }\n return\n }\n\n if (event.type === 'agent_message_chunk') {\n const block = event.content as { type?: string; text?: string; data?: string; mimeType?: string }\n if (block.type === 'text' && typeof block.text === 'string') {\n // A change in ACP messageId marks the previous assistant message as\n // complete. opencode streams each assistant message under its own id\n // with no delimiter chunk between them, so a change here is the only\n // boundary signal. Flush the previous message as its own Matrix\n // message — each ACP message lands separately (and interleaves with\n // tool_call/plan events) instead of welding into one turn-end blob.\n const prevMessageId = bufferMessageIds.get(event.sessionId)\n const messageChanged =\n event.messageId !== undefined &&\n prevMessageId !== undefined &&\n event.messageId !== prevMessageId\n if (event.messageId !== undefined)\n bufferMessageIds.set(event.sessionId, event.messageId)\n // flushBuffer clears the buffer synchronously, so the new message's\n // text below starts fresh.\n if (messageChanged) flushBuffer(event.sessionId)\n // Within a single message, tokens carry their own leading spaces, so we\n // concatenate raw. An empty chunk (some agents emit one between blocks,\n // e.g. after a tool call within the same message) is a paragraph break.\n const current = buffers.get(event.sessionId) ?? ''\n const needsBreak = current.length > 0 && block.text === ''\n const prefix = needsBreak ? '\\n\\n' : ''\n buffers.set(event.sessionId, current + prefix + block.text)\n } else if (\n block.type === 'image' &&\n typeof block.data === 'string' &&\n typeof block.mimeType === 'string' &&\n mediaClient\n ) {\n // Outbound agent image: upload immediately and send as a threaded m.image.\n const ctx = sessions.get(event.sessionId)\n if (ctx) {\n const bytes = Buffer.from(block.data, 'base64')\n const ext = (block.mimeType.split('/')[1] ?? 'png').replace(/[^a-z0-9]/gi, '')\n const filename = `image.${ext}`\n void mediaClient\n .upload({ data: bytes, contentType: block.mimeType, filename, asUserId: ctx.agent.userId })\n .then(({ content_uri }) =>\n client.sendMessage({\n roomId: ctx.roomId,\n asUserId: ctx.agent.userId,\n threadRoot: ctx.threadRoot,\n content: {\n msgtype: 'm.image',\n body: filename,\n url: content_uri,\n info: { mimetype: block.mimeType, size: bytes.length },\n },\n }),\n )\n .catch((err) => {\n console.warn(`[matrix:${name}] outbound image upload failed:`, err)\n void sendMediaError(ctx, err, 'agent image upload failed', client)\n })\n }\n } else {\n console.warn(`[matrix:${name}] dropped chunk block type=${block.type}`, block)\n }\n return\n }\n\n // An out-of-band event (tool_call / tool_call_update / plan) after some\n // buffered text means that assistant message is complete — flush it first\n // so it lands before this event on the wire, preserving interleaving.\n flushBuffer(event.sessionId)\n\n const eventType =\n event.type === 'tool_call'\n ? 'dev.zooid.tool_call'\n : event.type === 'tool_call_update'\n ? 'dev.zooid.tool_call_update'\n : event.type === 'available_commands'\n ? 'dev.zooid.available_commands_update'\n : 'dev.zooid.plan'\n const body =\n event.type === 'tool_call'\n ? toToolCallBody(event)\n : event.type === 'tool_call_update'\n ? toUpdateBody(event)\n : event.type === 'available_commands'\n ? toAvailableCommandsBody(event)\n : toPlanBody(event)\n body['m.relates_to'] = { rel_type: 'm.thread', event_id: ctx.threadRoot }\n const tail = (sendQueue.get(event.sessionId) ?? Promise.resolve()).then(async () => {\n try {\n await client.sendCustomEvent({\n roomId: ctx.roomId,\n asUserId: ctx.agent.userId,\n eventType,\n content: body,\n })\n } catch (err) {\n console.warn(`[matrix:${name}] sendCustomEvent(${eventType}) failed:`, err)\n }\n })\n sendQueue.set(event.sessionId, tail)\n await tail\n }\n\n agents.onApprovalRequest = async (name, req) => {\n const handle = approvals.register(name, (req as { sessionId: string }).sessionId, req, {\n timeoutMs: agents.getApprovalTimeoutMs(name),\n })\n return handle.decisionPromise\n }\n\n approvals.on('registered', (handle: RegisteredApproval) => {\n const ctx = sessions.get(handle.sessionId)\n if (!ctx) return\n const content: Record<string, unknown> = {\n approval_id: handle.approvalId,\n session_id: handle.sessionId,\n tool_call_id: handle.toolCallId,\n options: handle.options,\n }\n content['m.relates_to'] = { rel_type: 'm.thread', event_id: ctx.threadRoot }\n if (handle.toolKind !== undefined) content.tool_kind = handle.toolKind\n if (handle.toolTitle !== undefined) content.tool_title = handle.toolTitle\n if (handle.toolInput !== undefined) content.tool_input = handle.toolInput\n void client.sendCustomEvent({\n roomId: ctx.roomId,\n asUserId: ctx.agent.userId,\n eventType: 'dev.zooid.approval_request',\n content,\n })\n })\n\n async function handleInboundEvent(evt: MatrixEvent): Promise<void> {\n if (evt.event_id) {\n if (seenEventIds.has(evt.event_id)) {\n return\n }\n seenEventIds.add(evt.event_id)\n if (seenEventIds.size > SEEN_EVENT_CAP) {\n const first = seenEventIds.values().next().value\n if (first !== undefined) seenEventIds.delete(first)\n }\n }\n if (\n evt.origin_server_ts !== undefined &&\n evt.origin_server_ts < cutoffTs &&\n evt.type === 'm.room.message'\n ) {\n console.log(\n `[matrix] dropping stale message event ${evt.event_id} ` +\n `(ts=${evt.origin_server_ts}, daemon started at ${cutoffTs + STARTUP_GRACE_MS})`,\n )\n return\n }\n if (evt.type === 'm.room.member' && evt.content?.membership === 'invite') {\n const target = evt.state_key\n const inviter = evt.sender\n if (\n target &&\n evt.room_id &&\n ourBotUserIds.has(target) &&\n (!inviter || !ourBotUserIds.has(inviter))\n ) {\n console.log(\n `[matrix] declining ad-hoc invite for ${target} in ${evt.room_id} ` +\n `from ${inviter ?? 'unknown'}`,\n )\n await client\n .leaveRoom(evt.room_id, target, { reason: DECLINE_REASON })\n .catch((err) =>\n console.warn(`[matrix] leaveRoom(${evt.room_id}, ${target}) failed:`, err),\n )\n }\n return\n }\n if (evt.type === 'dev.zooid.session_reset') {\n // Spec § /clear: room-scope reset is unsupported. Only thread-scoped\n // resets carry a thread relation; drop bare room-level resets silently.\n const relates = evt.content?.['m.relates_to'] as\n | { rel_type?: string; event_id?: string }\n | undefined\n const threadRoot =\n relates?.rel_type === 'm.thread' && relates.event_id ? relates.event_id : undefined\n if (!threadRoot) {\n console.log('[matrix] dropping dev.zooid.session_reset without thread relation')\n return\n }\n console.log(`[matrix] inbound dev.zooid.session_reset in ${evt.room_id} thread=${threadRoot}`)\n // [[ZOD071]]: a thread's sessions are the thread-level one plus one per\n // handoff arc — end them all. Reset events aren't m.room.message, so\n // the self-heal rebuild above doesn't cover them; rebuild here if the\n // daemon restarted since the arcs were minted.\n if (!threadStates.has(threadRoot) && evt.room_id) {\n try {\n threadStates.set(\n threadRoot,\n await rebuildThreadState(client, evt.room_id, threadRoot, bindings),\n )\n } catch (err) {\n console.warn(`[matrix] failed to rebuild threadState for reset ${threadRoot}:`, err)\n }\n }\n const st = threadStates.get(threadRoot)\n for (const a of bindings) {\n agents.endSession(a.name, threadRoot)\n for (const arc of st?.handoffs[a.name] ?? []) {\n agents.endSession(a.name, composeHandoffKey(threadRoot, arc))\n }\n }\n // NB: keep threadStates intact. Per ZOD039 § /clear, only the agent's\n // session memory is wiped — thread-routing state (participants /\n // root-mentions) must survive so the next bare reply still routes to\n // the most-recently-posting agent under the same sessionKey.\n return\n }\n if (evt.type === 'dev.zooid.interrupt') {\n const content = (evt.content ?? {}) as { session_id?: string; reason?: string }\n // Thread-relation form (client-friendly): /interrupt in a thread sends\n // an empty event with `m.relates_to: thread/<root>`. Cancel every\n // session whose threadRoot matches.\n const relates = evt.content?.['m.relates_to'] as\n | { rel_type?: string; event_id?: string }\n | undefined\n const threadRoot =\n relates?.rel_type === 'm.thread' && relates.event_id ? relates.event_id : undefined\n if (threadRoot) {\n const targets: Array<{ sessionId: string; agent: string }> = []\n for (const [sessionId, ctx] of sessions) {\n if (ctx.threadRoot === threadRoot) {\n targets.push({ sessionId, agent: ctx.agent.name })\n }\n }\n for (const t of targets) {\n console.log(\n `[matrix] interrupt session=${t.sessionId} agent=${t.agent} thread=${threadRoot}` +\n (content.reason ? ` reason=${content.reason}` : ''),\n )\n await agents.cancelSession(t.agent, t.sessionId).catch((err) => {\n console.error(`[matrix] cancelSession(${t.agent}, ${t.sessionId}) failed:`, err)\n })\n }\n return\n }\n // Legacy form: explicit session_id in content.\n if (!content.session_id) {\n console.warn(`[matrix] dev.zooid.interrupt missing session_id (event_id=${evt.event_id})`)\n return\n }\n const ctx = sessions.get(content.session_id)\n if (!ctx) {\n return\n }\n console.log(\n `[matrix] interrupt session=${content.session_id} agent=${ctx.agent.name}` +\n (content.reason ? ` reason=${content.reason}` : ''),\n )\n await agents.cancelSession(ctx.agent.name, content.session_id).catch((err) => {\n console.error(`[matrix] cancelSession(${ctx.agent.name}, ${content.session_id}) failed:`, err)\n })\n return\n }\n if (evt.type === 'dev.zooid.approval_response') {\n const content = (evt.content ?? {}) as {\n approval_id?: string\n session_id?: string\n decision?: string\n option_id?: string\n }\n if (!content.session_id || !content.approval_id || !content.decision) return\n const decision = content.option_id\n ? { decision: content.decision, optionId: content.option_id }\n : { decision: content.decision }\n const ok = approvals.resolve(\n content.session_id,\n content.approval_id,\n decision as never,\n )\n if (!ok) console.warn(`[matrix] unknown approval ${content.approval_id}`)\n return\n }\n logInbound(evt)\n\n // Capture media events in the pending store; never route them to agents.\n if (\n evt.type === 'm.room.message' &&\n isMediaMsgtype(evt.content?.msgtype) &&\n evt.room_id &&\n evt.event_id &&\n evt.sender &&\n evt.content?.url &&\n !bindings.some((b) => b.userId === evt.sender)\n ) {\n pendingMedia.add(evt.room_id, inboundThreadRoot(evt), {\n eventId: evt.event_id,\n sender: evt.sender,\n msgtype: evt.content.msgtype as string,\n body: (evt.content.body as string | undefined) ?? '',\n filename: evt.content.filename as string | undefined,\n url: evt.content.url as string,\n info: evt.content.info as PendingMediaItem['info'],\n })\n return\n }\n\n // Agent-promotion: top-level inbound event becomes the thread root.\n // For in-thread messages the existing root is preserved.\n const promotedRoot = inboundThreadRoot(evt) ?? evt.event_id\n // Self-heal: if this is a thread reply but we have no in-memory state\n // for the root (e.g. daemon was just restarted), reconstruct it by\n // fetching the thread root + relations from the server.\n const inboundRel = inboundThreadRoot(evt)\n if (\n evt.type === 'm.room.message' &&\n inboundRel &&\n !threadStates.has(inboundRel) &&\n evt.room_id\n ) {\n try {\n const rebuilt = await rebuildThreadState(client, evt.room_id, inboundRel, bindings)\n threadStates.set(inboundRel, rebuilt)\n console.log(\n `[matrix] rebuilt threadState for ${inboundRel}: participants=${rebuilt.participants.join(',')} rootMentions=${rebuilt.rootMentions.join(',')}`,\n )\n } catch (err) {\n console.warn(`[matrix] failed to rebuild threadState for ${inboundRel}:`, err)\n }\n }\n const matches = route(evt, bindings, threadStates)\n // Suppress the no-match warning for events sent by our own bots.\n const senderIsBot = bindings.some((b) => b.userId === evt.sender)\n if (evt.type === 'm.room.message' && matches.length === 0 && !senderIsBot) {\n console.warn(\n `[matrix] no agent matched message in ${evt.room_id} from ${evt.sender}` +\n ` (bindings: ${bindings.map((b) => `${b.name}@${b.userId}[${b.trigger}]`).join(', ')})`,\n )\n }\n // Seed thread state for any agent mentions in this event.\n if (matches.length > 0 && promotedRoot) {\n let st = threadStates.get(promotedRoot)\n if (!st) {\n st = { participants: [], rootMentions: [], callers: {}, handoffs: {} }\n threadStates.set(promotedRoot, st)\n }\n const msgMentions = new Set(extractMentions(evt as never))\n const senderAgent = bindings.find((b) => b.userId === evt.sender)\n for (const a of bindings) {\n if (!msgMentions.has(a.userId)) continue\n if (!st.rootMentions.includes(a.name)) st.rootMentions.push(a.name)\n // Call edge: the (agent) sender is the caller of every agent it @mentions.\n if (senderAgent && a.name !== senderAgent.name) {\n st.callers[a.name] = senderAgent.name\n // [[ZOD071]] handoff arc: every agent→agent call opens a fresh\n // session for the callee, keyed by this call event. Recorded before\n // runTurn dispatch so sessionKeyFor resolves the just-minted arc.\n // Deduped: homeservers may redeliver a transaction.\n if (evt.event_id) {\n const arcs = (st.handoffs[a.name] ??= [])\n if (!arcs.includes(evt.event_id)) arcs.push(evt.event_id)\n }\n }\n }\n }\n for (const a of matches) {\n console.log(`[matrix] → ${a.name} (${a.userId})`)\n void runTurn(a, evt)\n .then(() => {\n if (!promotedRoot) return\n let st = threadStates.get(promotedRoot)\n if (!st) {\n st = { participants: [], rootMentions: [], callers: {}, handoffs: {} }\n threadStates.set(promotedRoot, st)\n }\n if (st.participants.at(-1) !== a.name) st.participants.push(a.name)\n })\n .catch((err) => {\n console.error(`[matrix] runTurn failed for ${a.name}:`, err)\n const c = classify(err)\n const threadRoot = inboundThreadRoot(evt) ?? evt.event_id\n if (!threadRoot || !evt.room_id) return\n const body = toErrorBody(\n {\n kind: 'error',\n agentId: a.name,\n sessionId: null,\n turnId: null,\n code: c.code,\n message: err instanceof Error ? err.message : String(err),\n detail: err instanceof Error && err.stack ? err.stack.slice(0, 2000) : undefined,\n transient: c.transient,\n acp_error: c.acp_error,\n },\n threadRoot,\n )\n void client\n .sendCustomEvent({\n roomId: evt.room_id,\n asUserId: a.userId,\n eventType: 'dev.zooid.error',\n content: body,\n })\n .catch((e) => console.warn(`[matrix:${a.name}] dev.zooid.error send failed:`, e))\n })\n }\n }\n\n const app = new Hono()\n\n function authOk(authHeader: string | undefined): boolean {\n const h = authHeader ?? ''\n if (!h.startsWith('Bearer ')) return false\n const got = h.slice(7)\n if (got.length !== hsToken.length) return false\n return timingSafeEqual(Buffer.from(got), Buffer.from(hsToken))\n }\n\n app.put('/_matrix/app/v1/transactions/:txnId', async (c) => {\n if (!authOk(c.req.header('authorization'))) {\n return c.json({ errcode: 'M_FORBIDDEN' }, 403)\n }\n const body = (await c.req.json().catch(() => ({}))) as { events?: MatrixEvent[] }\n for (const evt of body.events ?? []) {\n await handleInboundEvent(evt)\n }\n return c.json({})\n })\n\n app.get('/_matrix/app/v1/users/:userId', (c) => {\n if (!authOk(c.req.header('authorization'))) {\n return c.json({ errcode: 'M_FORBIDDEN' }, 403)\n }\n return c.json({})\n })\n app.get('/_matrix/app/v1/rooms/:alias', (c) => {\n if (!authOk(c.req.header('authorization'))) {\n return c.json({ errcode: 'M_FORBIDDEN' }, 403)\n }\n return c.json({ errcode: 'M_NOT_FOUND' }, 404)\n })\n app.post('/_matrix/app/v1/ping', (c) => {\n if (!authOk(c.req.header('authorization'))) {\n return c.json({ errcode: 'M_FORBIDDEN' }, 403)\n }\n return c.json({})\n })\n app.get('/healthz', (c) => c.text('ok'))\n\n async function runTurn(agent: AgentBinding, evt: MatrixEvent): Promise<void> {\n if (!evt.room_id || !evt.event_id) return\n const inbound = inboundThreadRoot(evt)\n // Agent-promotion: top-level inbound becomes a thread root via the agent's\n // first reply.\n const threadRoot = inbound ?? evt.event_id\n // [[ZOD071]]: the session key is the agent's current handoff arc when it\n // has one, else the thread-level key. The raw threadRoot still travels\n // separately: outbound events relate to it, and it is the context ref so\n // zooid_get_history reads the real thread.\n const sessionKey = sessionKeyFor(agent.name, threadRoot, threadStates.get(threadRoot))\n const sessionId = await agents.ensureSession(agent.name, sessionKey, evt.room_id, threadRoot)\n sessions.set(sessionId, { agent, roomId: evt.room_id, threadRoot })\n buffers.set(sessionId, '')\n bufferMessageIds.delete(sessionId)\n flushedCounts.set(sessionId, 0)\n // Commands the shim advertised during ensureSession (session load/new)\n // arrived before the ctx above existed and were stashed — replay the latest\n // now that the session is fully registered, so the palette actually fills.\n const stashedCommands = pendingCommands.get(sessionId)\n if (stashedCommands) {\n pendingCommands.delete(sessionId)\n void agents.onEvent?.(agent.name, stashedCommands)\n }\n\n const roomId = evt.room_id\n const TYPING_TTL_MS = 30_000\n const TYPING_REFRESH_MS = 25_000\n const safeTyping = (typing: boolean) =>\n client\n .setTyping({ roomId, asUserId: agent.userId, typing, timeoutMs: TYPING_TTL_MS })\n .catch((err) => console.warn(`[matrix:${agent.name}] setTyping(${typing}) failed:`, err))\n const safePresence = (presence: 'online' | 'unavailable' | 'offline') =>\n client\n .setPresence({ asUserId: agent.userId, presence })\n .catch((err) =>\n console.warn(`[matrix:${agent.name}] setPresence(${presence}) failed:`, err),\n )\n\n await safeTyping(true)\n await safePresence('unavailable')\n const refresh = setInterval(() => {\n void safeTyping(true)\n }, TYPING_REFRESH_MS)\n\n try {\n const rawBody = evt.content?.body ?? ''\n const promptText = stripMention(rawBody, agent.userId)\n\n // Drain pending media for this sender+thread and prepend as ACP content blocks.\n const pendingItems = pendingMedia.drain(\n evt.room_id,\n inboundThreadRoot(evt),\n evt.sender ?? '',\n )\n const { blocks, pathLines } = await buildMediaBlocks(pendingItems, {\n agent,\n media: mediaClient,\n writeAttachmentFn,\n onError: (item, err) => {\n console.warn(`[matrix:${agent.name}] media_failed for ${item.body}:`, err)\n void sendMediaError(\n { agent, roomId: evt.room_id!, threadRoot },\n err,\n `Could not process attachment: ${item.body}`,\n client,\n )\n },\n })\n\n const fullPromptText = [promptText, ...pathLines].filter(Boolean).join('\\n')\n await agents.prompt(agent.name, {\n threadId: sessionKey,\n channelId: evt.room_id,\n contextThreadId: threadRoot,\n content: [...blocks, { type: 'text', text: fullPromptText }],\n })\n // Drain: the prompt promise resolves on the stopReason response, but\n // trailing chunks may still arrive (see DRAIN_* above). Wait until the\n // buffer is quiet for DRAIN_QUIET_MS, re-arming on each new chunk.\n //\n // Subtlety: some agents (opencode in particular) resolve `session/prompt`\n // *before* the agent_message_chunk stream starts. So the buffer can be\n // empty for several seconds after prompt resolves, and only then do the\n // chunks arrive. We can't break the drain just because the buffer is\n // empty — we have to wait up to drainMaxMs for chunks to *start*. Once\n // any content arrives, the \"quiet for drainQuietMs\" rule kicks in.\n const drainStart = Date.now()\n let drained = buffers.get(sessionId) ?? ''\n while (drainQuietMs > 0 && Date.now() - drainStart < drainMaxMs) {\n await delay(drainQuietMs)\n const next = buffers.get(sessionId) ?? ''\n // Stop when the buffer is quiet (unchanged) and either it holds the\n // final message to flush, or we already flushed a message this turn\n // (so an empty, quiet buffer means the turn is genuinely done — the\n // last message was flushed mid-stream). An unchanged *empty* buffer\n // with nothing flushed yet means the stream hasn't started; keep\n // waiting up to drainMaxMs.\n if (next === drained && (next.length > 0 || (flushedCounts.get(sessionId) ?? 0) > 0))\n break\n drained = next\n }\n // Flush the final assistant message — the one with no following messageId\n // change or out-of-band event to have triggered an earlier flush.\n flushBuffer(sessionId)\n // Wait for every queued send (mid-turn flushes, tool/plan events, final\n // flush) to settle before tearing the session down.\n await (sendQueue.get(sessionId) ?? Promise.resolve())\n if ((flushedCounts.get(sessionId) ?? 0) === 0) {\n console.warn(\n `[matrix:${agent.name}] turn finished with empty buffer (session=${sessionId}); nothing sent to ${evt.room_id}`,\n )\n }\n } finally {\n clearInterval(refresh)\n await safeTyping(false)\n await safePresence('online')\n buffers.delete(sessionId)\n bufferMessageIds.delete(sessionId)\n flushedCounts.delete(sessionId)\n sendQueue.delete(sessionId)\n }\n }\n\n const syncLoops: SyncLoop[] | undefined =\n mode === 'client'\n ? bindings.map(\n (b) =>\n new SyncLoop({\n client: client as never,\n asUserId: b.userId,\n loadSince: () => opts.loadSince?.(b.userId) ?? null,\n saveSince: (since) => opts.saveSince?.(b.userId, since),\n onEvent: (evt) => handleInboundEvent(evt as MatrixEvent),\n }),\n )\n : undefined\n\n return {\n app,\n syncLoops,\n bootstrap: async (\n bootstrapOpts: {\n spaceRoomId?: string\n asUserId?: string\n adminUserIds?: string[]\n } = {},\n ) => {\n await pool.bootstrap({ adminUserId, ...bootstrapOpts })\n await Promise.allSettled(\n bindings.map((b) =>\n client.setPresence({ asUserId: b.userId, presence: 'online' }).catch((err) => {\n console.warn(`[matrix:${b.name}] initial setPresence(online) failed:`, err)\n }),\n ),\n )\n },\n pool,\n }\n}\n\n/**\n * Reconstruct the in-memory ThreadState for a thread root by fetching the\n * root event + its thread relations from the server. Used to recover the\n * implicit-routing rule from ZOD039 § Implicit triggers in threads after a\n * daemon restart wipes the in-memory cache.\n */\nexport async function rebuildThreadState(\n client: MatrixClient,\n roomId: string,\n rootEventId: string,\n bindings: AgentBinding[],\n): Promise<ThreadState> {\n const state: ThreadState = { participants: [], rootMentions: [], callers: {}, handoffs: {} }\n // Impersonate an agent that's actually a member of this room (AS reads\n // require room membership). Falling through to the first binding would\n // 403 if that agent never joined the target room.\n const asUser = (bindings.find((b) => b.rooms.some((r) => r.alias === roomId)) ?? bindings[0])?.userId\n if (!asUser) return state\n\n const root = await client.fetchEvent(roomId, rootEventId, asUser)\n if (root) {\n const rootMentions = new Set(extractMentions(root as never))\n const rootSender = (root as { sender?: string }).sender\n const rootSenderAgent = rootSender ? bindings.find((b) => b.userId === rootSender) : undefined\n for (const a of bindings) {\n if (!rootMentions.has(a.userId)) continue\n if (!state.rootMentions.includes(a.name)) state.rootMentions.push(a.name)\n if (rootSenderAgent && a.name !== rootSenderAgent.name) {\n state.callers[a.name] = rootSenderAgent.name\n const arcs = (state.handoffs[a.name] ??= [])\n if (!arcs.includes(rootEventId)) arcs.push(rootEventId)\n }\n }\n }\n\n const { chunk: thread } = await client.fetchThreadRelations({\n roomId,\n rootEventId,\n asUserId: asUser,\n })\n // Also seed root-mentions from any subsequent agent @mentions in the thread.\n for (const ev of thread) {\n const mentions = new Set(extractMentions(ev as never))\n const evSender = (ev as { sender?: string }).sender\n const evSenderAgent = evSender ? bindings.find((b) => b.userId === evSender) : undefined\n const evId = (ev as { event_id?: string }).event_id\n for (const a of bindings) {\n if (!mentions.has(a.userId)) continue\n if (!state.rootMentions.includes(a.name)) state.rootMentions.push(a.name)\n if (evSenderAgent && a.name !== evSenderAgent.name) {\n state.callers[a.name] = evSenderAgent.name\n if (evId) {\n const arcs = (state.handoffs[a.name] ??= [])\n if (!arcs.includes(evId)) arcs.push(evId)\n }\n }\n }\n const type = (ev as { type?: string }).type\n if (type === 'm.room.message' && evSender) {\n const a = bindings.find((b) => b.userId === evSender)\n if (a && state.participants.at(-1) !== a.name) state.participants.push(a.name)\n }\n }\n return state\n}\n\nfunction logInbound(evt: MatrixEvent): void {\n const sender = evt.sender ?? '?'\n const room = evt.room_id ?? '?'\n const type = evt.type ?? '?'\n if (type === 'm.room.message') {\n const body = evt.content?.body ?? ''\n const mentions = (evt.content?.['m.mentions'] as { user_ids?: string[] } | undefined)?.user_ids\n const mentionsStr = mentions?.length ? ` mentions=${JSON.stringify(mentions)}` : ''\n console.log(\n `[matrix] inbound msg in ${room} from ${sender}${mentionsStr}: ${truncate(body, 200)}`,\n )\n } else {\n console.log(`[matrix] inbound ${type} in ${room} from ${sender}`)\n }\n}\n\nfunction truncate(s: string, n: number): string {\n return s.length > n ? s.slice(0, n) + '…' : s\n}\n","import type { ThreadState } from './router.js'\n\n/**\n * Separator between a thread root and a handoff-arc call event id inside a\n * session key. Matrix event IDs ('$' + URL-safe base64; legacy '$hash:domain')\n * never contain '|', so composed keys are unambiguous — though nothing below\n * the transport ever parses one: the key is opaque downstream ([[ZOD071]]).\n */\nexport const HANDOFF_KEY_SEP = '|'\n\nexport function composeHandoffKey(threadRoot: string, callEventId: string): string {\n return `${threadRoot}${HANDOFF_KEY_SEP}${callEventId}`\n}\n\n/**\n * The session key for an agent's next turn in a thread: its latest handoff\n * arc when it has been called (agent→agent @mention, [[ZOD071]]), else the\n * thread-level key. Relies on the transaction handler recording the arc\n * BEFORE the turn is dispatched, so a just-called sub resolves to the arc\n * minted by its own triggering event.\n */\nexport function sessionKeyFor(\n agentName: string,\n threadRoot: string,\n state: ThreadState | undefined,\n): string {\n const arc = state?.handoffs[agentName]?.at(-1)\n return arc ? composeHandoffKey(threadRoot, arc) : threadRoot\n}\n","import type {\n AvailableCommandsEvent,\n PlanEvent,\n TapEvent,\n ToolCallEvent,\n ToolCallUpdateEvent,\n} from '@zooid/acp-client'\n\n/** Cap any single string in rawInput so big diffs / file contents don't bloat Matrix. */\nconst RAW_INPUT_STR_MAX = 250\n\nexport function toToolCallBody(evt: ToolCallEvent): Record<string, unknown> {\n const out: Record<string, unknown> = {\n session_id: evt.sessionId,\n tool_call_id: evt.toolCallId,\n title: evt.title,\n }\n if (evt.kind !== undefined) out.kind = evt.kind\n if (evt.status !== undefined) out.status = evt.status\n if (evt.rawInput !== undefined) out.raw_input = truncateStrings(evt.rawInput, RAW_INPUT_STR_MAX)\n if (evt.locations !== undefined) out.locations = evt.locations\n return out\n}\n\n/**\n * Recursively truncates string values longer than `max` with a \"… [truncated]\"\n * suffix. Non-string scalars and structure are preserved.\n */\nfunction truncateStrings(v: unknown, max: number): unknown {\n if (typeof v === 'string') {\n return v.length > max ? v.slice(0, max) + '… [truncated]' : v\n }\n if (Array.isArray(v)) {\n return v.map((item) => truncateStrings(item, max))\n }\n if (v && typeof v === 'object') {\n const out: Record<string, unknown> = {}\n for (const [k, val] of Object.entries(v as Record<string, unknown>)) {\n out[k] = truncateStrings(val, max)\n }\n return out\n }\n return v\n}\n\nexport function toUpdateBody(evt: ToolCallUpdateEvent): Record<string, unknown> {\n const out: Record<string, unknown> = {\n session_id: evt.sessionId,\n tool_call_id: evt.toolCallId,\n }\n if (evt.status !== undefined) out.status = evt.status\n if (evt.kind !== undefined) out.kind = evt.kind\n // content[] carries display-ready output (text/diff/terminal); rawOutput is\n // intentionally NOT serialized — it's typically large and duplicates content.\n if (evt.content !== undefined) out.content = evt.content\n // Some ACP agents only set rawInput on a later update (not the initial\n // tool_call). Truncate strings and forward.\n if (evt.rawInput !== undefined) out.raw_input = truncateStrings(evt.rawInput, RAW_INPUT_STR_MAX)\n if (evt.locations !== undefined) out.locations = evt.locations\n return out\n}\n\nexport function toPlanBody(evt: PlanEvent): Record<string, unknown> {\n return {\n session_id: evt.sessionId,\n entries: evt.entries,\n }\n}\n\nexport function toAvailableCommandsBody(\n evt: AvailableCommandsEvent,\n): Record<string, unknown> {\n return {\n session_id: evt.sessionId,\n available_commands: evt.commands.map((c) => ({\n name: c.name,\n description: c.description,\n })),\n }\n}\n\nconst RECOVERY_URLS: Partial<Record<string, string>> = {\n auth_missing: 'https://zooid.dev/docs/guides/run-in-container#authentication-that-carries-over',\n auth_invalid: 'https://zooid.dev/docs/guides/run-in-container#authentication-that-carries-over',\n mount_failed: 'https://zooid.dev/docs/guides/run-in-container#what-you-get-for-free',\n image_pull_failed: 'https://zooid.dev/docs/guides/run-in-container#skipping-the-image-prepull',\n}\n\ntype ErrorTap = Extract<TapEvent, { kind: 'error' }>\n\nexport function toErrorBody(evt: ErrorTap, threadRoot: string): Record<string, unknown> {\n const msg = evt.message.slice(0, 250)\n const out: Record<string, unknown> = {\n msgtype: 'm.notice',\n body: `⚠ [${evt.code}] ${msg}`,\n code: evt.code,\n message: msg,\n transient: evt.transient,\n 'm.relates_to': { rel_type: 'm.thread', event_id: threadRoot },\n }\n if (evt.sessionId) out.session_id = evt.sessionId\n if (evt.turnId) out.turn_id = evt.turnId\n if (evt.detail) out.detail = evt.detail.slice(0, 2000)\n if (evt.acp_error) out.acp_error = evt.acp_error\n const recovery = RECOVERY_URLS[evt.code]\n if (recovery) out.recovery = recovery\n return out\n}\n","import { marked } from 'marked'\nimport sanitizeHtml from 'sanitize-html'\n\n// Matrix-permitted HTML tags per the m.room.message spec.\n// https://spec.matrix.org/v1.11/client-server-api/#mroommessage-msgtypes\nconst ALLOWED_TAGS = [\n 'del', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'blockquote', 'p', 'a',\n 'ul', 'ol', 'sup', 'sub', 'li', 'b', 'i', 'u', 'strong', 'em', 's',\n 'code', 'hr', 'br', 'div', 'table', 'thead', 'tbody', 'tr', 'th',\n 'td', 'caption', 'pre', 'span', 'img', 'details', 'summary',\n]\n\nconst ALLOWED_ATTRIBUTES: Record<string, string[]> = {\n a: ['href', 'name', 'target'],\n img: ['width', 'height', 'alt', 'title', 'src'],\n ol: ['start'],\n code: ['class'],\n span: ['data-mx-color', 'data-mx-bg-color', 'data-mx-spoiler'],\n}\n\nconst ALLOWED_SCHEMES = ['https', 'http', 'ftp', 'mailto', 'magnet', 'matrix']\n\nmarked.setOptions({ gfm: true, breaks: false, async: false })\n\nexport function toMatrixHtml(markdown: string): string {\n if (!markdown || !markdown.trim()) return ''\n let rawHtml: string\n try {\n const out = marked.parse(markdown) as string | Promise<string>\n if (typeof out !== 'string') return ''\n rawHtml = out\n } catch {\n return ''\n }\n return sanitizeHtml(rawHtml, {\n allowedTags: ALLOWED_TAGS,\n allowedAttributes: ALLOWED_ATTRIBUTES,\n allowedSchemes: ALLOWED_SCHEMES,\n allowedSchemesByTag: { a: ALLOWED_SCHEMES },\n })\n}\n","export const MAX_MEDIA_PER_TURN = 8\n\nexport interface PendingMediaItem {\n eventId: string\n sender: string\n msgtype: string\n body: string\n filename?: string\n url: string\n info?: { mimetype?: string; size?: number; w?: number; h?: number }\n}\n\nexport class PendingMediaStore {\n private readonly queues = new Map<string, PendingMediaItem[]>()\n\n private key(roomId: string, threadKey: string | undefined): string {\n return `${roomId} ${threadKey ?? 'main'}`\n }\n\n add(roomId: string, threadKey: string | undefined, item: PendingMediaItem): void {\n const k = this.key(roomId, threadKey)\n const q = this.queues.get(k) ?? []\n q.push(item)\n while (q.length > MAX_MEDIA_PER_TURN) q.shift()\n this.queues.set(k, q)\n }\n\n drain(roomId: string, threadKey: string | undefined, sender: string): PendingMediaItem[] {\n const k = this.key(roomId, threadKey)\n const q = this.queues.get(k) ?? []\n const mine = q.filter((i) => i.sender === sender)\n const rest = q.filter((i) => i.sender !== sender)\n if (rest.length) this.queues.set(k, rest)\n else this.queues.delete(k)\n return mine\n }\n}\n","/** Limits are routing policy, not enforcement — see ZOD057 (enforcement lives\n * in Tuwunel config + the Zoon composer). */\nexport const MAX_INLINE_IMAGE_BYTES = 524_288\nexport const INLINE_IMAGE_MIMES = ['image/png', 'image/jpeg', 'image/gif', 'image/webp']\nexport const MAX_DOWNLOAD_BYTES = 33_554_432\n\nexport interface MediaClientOptions {\n homeserver: string\n asToken: string\n fetch?: typeof globalThis.fetch\n}\n\nexport function parseMxcUri(uri: string): { serverName: string; mediaId: string } | null {\n const m = /^mxc:\\/\\/([^/]+)\\/(.+)$/.exec(uri)\n return m ? { serverName: m[1], mediaId: m[2] } : null\n}\n\nexport class MediaClient {\n private readonly homeserver: string\n private readonly asToken: string\n private readonly fetch: typeof globalThis.fetch\n\n constructor(opts: MediaClientOptions) {\n this.homeserver = opts.homeserver.replace(/\\/$/, '')\n this.asToken = opts.asToken\n this.fetch = opts.fetch ?? globalThis.fetch\n }\n\n async download(input: {\n mxcUri: string\n asUserId: string\n maxBytes?: number\n }): Promise<{ data: Uint8Array; contentType: string }> {\n const parsed = parseMxcUri(input.mxcUri)\n if (!parsed) throw new Error(`not an mxc uri: ${input.mxcUri}`)\n const url =\n `${this.homeserver}/_matrix/client/v1/media/download/` +\n `${encodeURIComponent(parsed.serverName)}/${encodeURIComponent(parsed.mediaId)}` +\n `?user_id=${encodeURIComponent(input.asUserId)}`\n const r = await this.fetch(url, {\n headers: { Authorization: `Bearer ${this.asToken}` },\n })\n if (!r.ok) throw new Error(`media download failed: ${r.status}`)\n const buf = new Uint8Array(await r.arrayBuffer())\n const max = input.maxBytes ?? MAX_DOWNLOAD_BYTES\n if (buf.byteLength > max) {\n throw new Error(`media too large: ${buf.byteLength} > ${max}`)\n }\n return { data: buf, contentType: r.headers.get('content-type') ?? 'application/octet-stream' }\n }\n\n async upload(input: {\n data: Uint8Array\n contentType: string\n filename?: string\n asUserId: string\n }): Promise<{ content_uri: string }> {\n const params = new URLSearchParams()\n if (input.filename) params.set('filename', input.filename)\n params.set('user_id', input.asUserId)\n const r = await this.fetch(`${this.homeserver}/_matrix/media/v3/upload?${params}`, {\n method: 'POST',\n headers: { Authorization: `Bearer ${this.asToken}`, 'Content-Type': input.contentType },\n body: input.data,\n })\n if (!r.ok) throw new Error(`media upload failed: ${r.status}`)\n return (await r.json()) as { content_uri: string }\n }\n}\n","import { mkdirSync, writeFileSync } from 'node:fs'\nimport { join } from 'node:path'\nimport { posix } from 'node:path'\n\nfunction sanitize(s: string, fallback: string): string {\n const cleaned = s.replace(/[^A-Za-z0-9._-]/g, '').replace(/^\\.+/, '')\n return cleaned || fallback\n}\n\nexport interface WriteAttachmentInput {\n workspaceDir: string\n agentWorkspacePath: string\n eventId: string\n filename: string\n data: Uint8Array\n}\n\nexport function writeAttachment(input: WriteAttachmentInput): {\n hostPath: string\n agentPath: string\n} {\n const dir = sanitize(input.eventId, 'event')\n const name = sanitize(input.filename, 'file')\n const hostDir = join(input.workspaceDir, '.zooid', 'attachments', dir)\n mkdirSync(hostDir, { recursive: true })\n const hostPath = join(hostDir, name)\n writeFileSync(hostPath, input.data)\n const agentPath = posix.join(input.agentWorkspacePath, '.zooid', 'attachments', dir, name)\n return { hostPath, agentPath }\n}\n","export interface SyncResponse {\n next_batch: string\n // Real homeservers omit `rooms` (and `rooms.join`) entirely on an idle\n // incremental sync — both are optional.\n rooms?: {\n join?: Record<string, {\n // A joined room may carry only state/ephemeral/account_data on a given\n // sync, with no `timeline` (or a timeline with no `events`).\n timeline?: {\n events?: Record<string, unknown>[]\n prev_batch?: string\n limited?: boolean\n }\n }>\n }\n}\n\nexport interface SyncClient {\n sync(opts: { asUserId: string; since: string | null; timeoutMs: number }): Promise<SyncResponse>\n}\n\nexport interface SyncLoopOptions {\n client: SyncClient\n asUserId: string\n loadSince: () => string | null\n saveSince: (since: string) => void\n onEvent: (evt: Record<string, unknown>) => void | Promise<void>\n timeoutMs?: number\n /** Backoff after a failed tick before retrying. Default 5000ms. */\n retryDelayMs?: number\n}\n\nexport class SyncLoop {\n private readonly opts: SyncLoopOptions\n private running = false\n\n constructor(opts: SyncLoopOptions) {\n this.opts = opts\n }\n\n async tick(): Promise<void> {\n const since = this.opts.loadSince()\n const res = await this.opts.client.sync({\n asUserId: this.opts.asUserId,\n since,\n timeoutMs: this.opts.timeoutMs ?? 30_000,\n })\n for (const [roomId, roomState] of Object.entries(res.rooms?.join ?? {})) {\n for (const baseEvt of roomState.timeline?.events ?? []) {\n await this.opts.onEvent({ ...(baseEvt as Record<string, unknown>), room_id: roomId })\n }\n }\n this.opts.saveSince(res.next_batch)\n }\n\n async run(): Promise<void> {\n this.running = true\n while (this.running) {\n try {\n await this.tick()\n } catch (err) {\n // A transient /sync failure (network blip, sleep/wake, 5xx) must not\n // kill the loop — log and back off, then resume from the same `since`.\n if (!this.running) break\n console.warn(`[sync-loop] ${this.opts.asUserId} tick failed, retrying:`, err)\n await new Promise((r) => setTimeout(r, this.opts.retryDelayMs ?? 5_000))\n }\n }\n }\n\n stop(): void {\n this.running = false\n }\n}\n","import { MatrixClient } from './matrix-client.js'\nimport type { AgentBinding } from './router.js'\n\nexport interface WorkforceRoster {\n version: 1\n agents: { user_id: string; name: string; rooms: string[] }[]\n}\n\nexport function buildWorkforceRoster(agents: AgentBinding[]): WorkforceRoster {\n return {\n version: 1,\n agents: agents.map((a) => ({\n user_id: a.userId,\n name: a.name,\n rooms: a.rooms.map((r) => r.alias),\n })),\n }\n}\n\nexport interface PublishOpts {\n client: MatrixClient\n spaceRoomId: string\n asUserId: string\n agents: AgentBinding[]\n}\n\nexport async function publishWorkforce(opts: PublishOpts): Promise<void> {\n await opts.client.sendStateEvent({\n roomId: opts.spaceRoomId,\n asUserId: opts.asUserId,\n eventType: 'dev.zooid.workforce',\n stateKey: '',\n content: buildWorkforceRoster(opts.agents) as unknown as Record<string, unknown>,\n })\n}\n\nexport interface PublisherHandle {\n reload(): Promise<void>\n stop(): Promise<void>\n}\n\nexport interface StartOpts {\n client: MatrixClient\n spaceRoomId: string\n asUserId: string\n getAgents: () => AgentBinding[]\n}\n\nexport async function startWorkforcePublisher(opts: StartOpts): Promise<PublisherHandle> {\n await publishWorkforce({ ...opts, agents: opts.getAgents() })\n return {\n async reload() {\n await publishWorkforce({ ...opts, agents: opts.getAgents() })\n },\n async stop() {},\n }\n}\n"],"mappings":"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","names":["localpart","body","localpart","localpart","inboundThreadRoot","ctx"]}
1
+ {"version":3,"sources":["../src/matrix-client.ts","../src/context-provider.ts","../src/registration.ts","../src/identity.ts","../src/mentions.ts","../src/router.ts","../src/space-provisioner.ts","../src/bot-pool.ts","../src/transport.ts","../src/session-keys.ts","../src/event-encoders.ts","../src/markdown-to-matrix-html.ts","../src/pending-media.ts","../src/media-client.ts","../src/attachments.ts","../src/sync-loop.ts","../src/workforce-publisher.ts"],"sourcesContent":["import { randomUUID } from 'node:crypto'\n\nexport interface MatrixClientOptions {\n homeserver: string\n asToken: string\n fetch?: typeof globalThis.fetch\n}\n\nexport interface SendMessageInput {\n roomId: string\n asUserId: string\n content: { msgtype: string; body: string; [k: string]: unknown }\n threadRoot?: string\n}\n\nexport interface SendCustomEventInput {\n roomId: string\n asUserId: string\n eventType: string\n content: Record<string, unknown>\n}\n\nexport interface SetTypingInput {\n roomId: string\n asUserId: string\n typing: boolean\n /** ms — homeserver expects re-PUTs before this expires. Ignored when typing=false. */\n timeoutMs?: number\n}\n\nexport interface SetPresenceInput {\n asUserId: string\n presence: 'online' | 'unavailable' | 'offline'\n statusMsg?: string\n}\n\nexport class MatrixClient {\n private readonly homeserver: string\n private readonly asToken: string\n private readonly fetch: typeof globalThis.fetch\n\n constructor(opts: MatrixClientOptions) {\n this.homeserver = opts.homeserver.replace(/\\/$/, '')\n this.asToken = opts.asToken\n this.fetch = opts.fetch ?? globalThis.fetch\n }\n\n async registerBot(\n localpart: string,\n ): Promise<{ user_id: string; device_id: string } | undefined> {\n const r = await this.fetch(`${this.homeserver}/_matrix/client/v3/register`, {\n method: 'POST',\n headers: { Authorization: `Bearer ${this.asToken}` },\n body: JSON.stringify({ type: 'm.login.application_service', username: localpart }),\n })\n if (r.status === 200) return (await r.json()) as { user_id: string; device_id: string }\n if (r.status === 400) {\n const body = (await r.json()) as { errcode?: string }\n if (body.errcode === 'M_USER_IN_USE') return undefined\n }\n throw new Error(`registerBot(${localpart}) failed: ${r.status}`)\n }\n\n async resolveAlias(alias: string): Promise<string | null> {\n const r = await this.fetch(\n `${this.homeserver}/_matrix/client/v3/directory/room/${encodeURIComponent(alias)}`,\n { headers: { Authorization: `Bearer ${this.asToken}` } },\n )\n if (r.status === 404) return null\n if (!r.ok) throw new Error(`resolveAlias(${alias}) failed: ${r.status}`)\n const j = (await r.json()) as { room_id: string }\n return j.room_id\n }\n\n async createRoom(opts: {\n roomAliasName: string\n invite: string[]\n senderUserId: string\n preset?: 'public_chat' | 'private_chat' | 'trusted_private_chat'\n /** Optional `m.room.name`. When set, sent in the createRoom body so the\n * room has a display name from the moment it exists. */\n name?: string\n /** When set, the room is created with a `restricted` join rule whose allow\n * condition references this space room ID — i.e. joinable by space members\n * only, rather than the whole homeserver. */\n restrictedToSpaceId?: string\n /** Explicit room version. Restricted join rules require v8+; omit to use the\n * homeserver default (modern Tuwunel defaults to v10/v11). */\n roomVersion?: string\n /**\n * Seeds `m.room.power_levels.users` at creation via\n * `power_level_content_override.users`. The caller owns the full map —\n * typically the AS bot at 100, plus operator and any agents with\n * declared PLs. Empty/absent → no override (the preset's defaults apply).\n */\n userPowerLevels?: Record<string, number>\n }): Promise<string> {\n const body: Record<string, unknown> = {\n room_alias_name: opts.roomAliasName,\n invite: opts.invite,\n preset: opts.preset ?? 'public_chat',\n }\n if (opts.name !== undefined) body.name = opts.name\n if (opts.roomVersion !== undefined) body.room_version = opts.roomVersion\n if (opts.restrictedToSpaceId !== undefined) {\n body.initial_state = [\n {\n type: 'm.room.join_rules',\n state_key: '',\n content: {\n join_rule: 'restricted',\n allow: [{ type: 'm.room_membership', room_id: opts.restrictedToSpaceId }],\n },\n },\n ]\n }\n if (opts.userPowerLevels && Object.keys(opts.userPowerLevels).length > 0) {\n // `power_level_content_override.users` REPLACES the default `{ creator: 100 }`\n // map rather than merging into it. If the creator (senderUserId) is omitted\n // here they drop to `users_default` (0) and can't set `m.room.canonical_alias`\n // (PL 50) during creation — the createRoom call then 403s and leaves an\n // aliasless orphan room. Always keep the creator at admin power.\n const users = { ...opts.userPowerLevels }\n if (users[opts.senderUserId] === undefined) users[opts.senderUserId] = 100\n body.power_level_content_override = { users }\n }\n const r = await this.fetch(\n `${this.homeserver}/_matrix/client/v3/createRoom?user_id=${encodeURIComponent(opts.senderUserId)}`,\n {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${this.asToken}`,\n 'content-type': 'application/json',\n },\n body: JSON.stringify(body),\n },\n )\n if (!r.ok) {\n const body = await r.text()\n throw new Error(`createRoom(${opts.roomAliasName}) failed: ${r.status} ${body}`)\n }\n const j = (await r.json()) as { room_id: string }\n return j.room_id\n }\n\n async createRoomRaw(opts: {\n asUserId: string\n body: Record<string, unknown>\n }): Promise<string> {\n const url = `${this.homeserver}/_matrix/client/v3/createRoom?user_id=${encodeURIComponent(opts.asUserId)}`\n const r = await this.fetch(url, {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${this.asToken}`,\n 'content-type': 'application/json',\n },\n body: JSON.stringify(opts.body),\n })\n if (!r.ok) throw new Error(`createRoomRaw failed: ${r.status}`)\n const j = (await r.json()) as { room_id: string }\n return j.room_id\n }\n\n async sendStateEvent(opts: {\n roomId: string\n asUserId: string\n eventType: string\n stateKey?: string\n content: Record<string, unknown>\n }): Promise<{ event_id: string }> {\n const url =\n `${this.homeserver}/_matrix/client/v3/rooms/${encodeURIComponent(opts.roomId)}` +\n `/state/${encodeURIComponent(opts.eventType)}/${encodeURIComponent(opts.stateKey ?? '')}` +\n `?user_id=${encodeURIComponent(opts.asUserId)}`\n const r = await this.fetch(url, {\n method: 'PUT',\n headers: {\n Authorization: `Bearer ${this.asToken}`,\n 'content-type': 'application/json',\n },\n body: JSON.stringify(opts.content),\n })\n if (!r.ok) throw new Error(`sendStateEvent ${opts.eventType} failed: ${r.status}`)\n return (await r.json()) as { event_id: string }\n }\n\n /**\n * Invite a user to a room. Sent as the inviter (`asUserId`) — that user\n * needs invite power in the room. Tolerates the \"already in room /\n * already invited\" responses idempotently so bootstrap can run on a\n * fresh AND a populated homeserver without branching.\n */\n async invite(opts: {\n roomId: string\n asUserId: string\n targetUserId: string\n }): Promise<void> {\n const url =\n `${this.homeserver}/_matrix/client/v3/rooms/${encodeURIComponent(opts.roomId)}/invite` +\n `?user_id=${encodeURIComponent(opts.asUserId)}`\n const r = await this.fetch(url, {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${this.asToken}`,\n 'content-type': 'application/json',\n },\n body: JSON.stringify({ user_id: opts.targetUserId }),\n })\n if (r.ok) return\n if (r.status === 403) {\n // Tuwunel/Synapse use M_FORBIDDEN both for permission errors AND for\n // \"already a member / already invited\". Inspect the body so we only\n // swallow the idempotent case.\n //\n // Phrasings seen in the wild:\n // - Synapse: \"is already in the room\", \"is already invited\"\n // - Tuwunel: \"cannot invite user that is joined or banned\" (one\n // string for both \"joined\" and \"banned\" — we can't tell which\n // from the body; the bot-pool's outer try-catch surfaces a real\n // ban via the subsequent joinRoom failure)\n // - Generic: \"X is already a member of the room\"\n const body = await r.text()\n const idempotent =\n /already (in the room|invited|a member|joined)/i.test(body) ||\n /user that is joined/i.test(body)\n if (idempotent) return\n throw new Error(`invite(${opts.targetUserId}) failed: 403 ${body}`)\n }\n throw new Error(`invite(${opts.targetUserId}) failed: ${r.status}`)\n }\n\n async leaveRoom(\n roomId: string,\n asUserId: string,\n opts?: { reason?: string },\n ): Promise<void> {\n const url =\n `${this.homeserver}/_matrix/client/v3/rooms/${encodeURIComponent(roomId)}/leave` +\n `?user_id=${encodeURIComponent(asUserId)}`\n const r = await this.fetch(url, {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${this.asToken}`,\n 'content-type': 'application/json',\n },\n body: JSON.stringify(opts?.reason ? { reason: opts.reason } : {}),\n })\n if (!r.ok) throw new Error(`leaveRoom(${roomId}, ${asUserId}) failed: ${r.status}`)\n }\n\n async joinRoom(roomIdOrAlias: string, asUserId: string): Promise<void> {\n const url =\n `${this.homeserver}/_matrix/client/v3/join/${encodeURIComponent(roomIdOrAlias)}` +\n `?user_id=${encodeURIComponent(asUserId)}`\n const r = await this.fetch(url, {\n method: 'POST',\n headers: { Authorization: `Bearer ${this.asToken}` },\n body: '{}',\n })\n if (!r.ok) throw new Error(`joinRoom(${roomIdOrAlias}) failed: ${r.status}`)\n }\n\n async sendMessage(input: SendMessageInput): Promise<{ event_id: string }> {\n const content: Record<string, unknown> = { ...input.content }\n if (input.threadRoot) {\n content['m.relates_to'] = { rel_type: 'm.thread', event_id: input.threadRoot }\n }\n return this.sendEvent(input.roomId, input.asUserId, 'm.room.message', content)\n }\n\n async sendCustomEvent(input: SendCustomEventInput): Promise<{ event_id: string }> {\n return this.sendEvent(input.roomId, input.asUserId, input.eventType, input.content)\n }\n\n async setTyping(input: SetTypingInput): Promise<void> {\n const url =\n `${this.homeserver}/_matrix/client/v3/rooms/${encodeURIComponent(input.roomId)}` +\n `/typing/${encodeURIComponent(input.asUserId)}` +\n `?user_id=${encodeURIComponent(input.asUserId)}`\n const body: Record<string, unknown> = { typing: input.typing }\n if (input.typing && input.timeoutMs !== undefined) body.timeout = input.timeoutMs\n const r = await this.fetch(url, {\n method: 'PUT',\n headers: { Authorization: `Bearer ${this.asToken}` },\n body: JSON.stringify(body),\n })\n if (!r.ok) throw new Error(`setTyping(${input.roomId}, ${input.asUserId}) failed: ${r.status}`)\n }\n\n async setDisplayName(asUserId: string, displayName: string): Promise<void> {\n const url =\n `${this.homeserver}/_matrix/client/v3/profile/${encodeURIComponent(asUserId)}/displayname` +\n `?user_id=${encodeURIComponent(asUserId)}`\n const r = await this.fetch(url, {\n method: 'PUT',\n headers: {\n Authorization: `Bearer ${this.asToken}`,\n 'content-type': 'application/json',\n },\n body: JSON.stringify({ displayname: displayName }),\n })\n if (!r.ok) throw new Error(`setDisplayName(${asUserId}) failed: ${r.status}`)\n }\n\n async setPresence(input: SetPresenceInput): Promise<void> {\n const url =\n `${this.homeserver}/_matrix/client/v3/presence/${encodeURIComponent(input.asUserId)}/status` +\n `?user_id=${encodeURIComponent(input.asUserId)}`\n const body: Record<string, unknown> = { presence: input.presence }\n if (input.statusMsg !== undefined) body.status_msg = input.statusMsg\n const r = await this.fetch(url, {\n method: 'PUT',\n headers: { Authorization: `Bearer ${this.asToken}` },\n body: JSON.stringify(body),\n })\n if (!r.ok) throw new Error(`setPresence(${input.asUserId}) failed: ${r.status}`)\n }\n\n /**\n * Fetch a single event from a room. Used to recover thread-root context\n * after a daemon restart wipes the in-memory threadStates cache.\n */\n async fetchEvent(\n roomId: string,\n eventId: string,\n asUserId: string,\n ): Promise<Record<string, unknown> | null> {\n const url =\n `${this.homeserver}/_matrix/client/v3/rooms/${encodeURIComponent(roomId)}` +\n `/event/${encodeURIComponent(eventId)}` +\n `?user_id=${encodeURIComponent(asUserId)}`\n const r = await this.fetch(url, {\n method: 'GET',\n headers: { Authorization: `Bearer ${this.asToken}` },\n })\n if (r.status === 404) return null\n if (!r.ok) throw new Error(`fetchEvent(${eventId}) failed: ${r.status}`)\n return (await r.json()) as Record<string, unknown>\n }\n\n /**\n * Fetch replies to a thread root via the relations endpoint, oldest-first.\n * Pass `limit` and `from` for pagination; `next_batch` echoes back when\n * there are more replies. Returns `{ chunk: [] }` when the root is unknown.\n */\n async fetchThreadRelations(opts: {\n roomId: string\n rootEventId: string\n asUserId: string\n limit?: number\n from?: string\n }): Promise<{ chunk: Array<Record<string, unknown>>; next_batch?: string }> {\n const params = new URLSearchParams({\n dir: 'f',\n limit: String(opts.limit ?? 100),\n user_id: opts.asUserId,\n })\n if (opts.from) params.set('from', opts.from)\n const url =\n `${this.homeserver}/_matrix/client/v1/rooms/${encodeURIComponent(opts.roomId)}` +\n `/relations/${encodeURIComponent(opts.rootEventId)}/m.thread?${params.toString()}`\n const r = await this.fetch(url, {\n method: 'GET',\n headers: { Authorization: `Bearer ${this.asToken}` },\n })\n if (r.status === 404) return { chunk: [] }\n if (!r.ok) throw new Error(`fetchThreadRelations(${opts.rootEventId}) failed: ${r.status}`)\n const body = (await r.json()) as {\n chunk?: Array<Record<string, unknown>>\n next_batch?: string\n }\n return { chunk: body.chunk ?? [], next_batch: body.next_batch }\n }\n\n /**\n * Paginate the room timeline. Returns events newest-first (dir=b) per Matrix\n * spec. The caller is responsible for reversing if it wants oldest-first.\n */\n async fetchRoomMessages(opts: {\n roomId: string\n asUserId: string\n limit?: number\n /** Opaque pagination token returned in `end` from a previous call. */\n from?: string\n /**\n * Server-side RoomEventFilter. Common keys: `types` (whitelist event types),\n * `not_types`, `not_rel_types` (e.g. `['m.thread']` to exclude thread\n * replies). Encoded as JSON into the `filter` query param.\n */\n filter?: {\n types?: string[]\n not_types?: string[]\n rel_types?: string[]\n not_rel_types?: string[]\n }\n }): Promise<{ chunk: Array<Record<string, unknown>>; end?: string }> {\n const params = new URLSearchParams({\n dir: 'b',\n limit: String(opts.limit ?? 50),\n user_id: opts.asUserId,\n })\n if (opts.from) params.set('from', opts.from)\n if (opts.filter) params.set('filter', JSON.stringify(opts.filter))\n const url =\n `${this.homeserver}/_matrix/client/v3/rooms/${encodeURIComponent(opts.roomId)}` +\n `/messages?${params.toString()}`\n const r = await this.fetch(url, {\n method: 'GET',\n headers: { Authorization: `Bearer ${this.asToken}` },\n })\n if (!r.ok) throw new Error(`fetchRoomMessages(${opts.roomId}) failed: ${r.status}`)\n return (await r.json()) as { chunk: Array<Record<string, unknown>>; end?: string }\n }\n\n async getJoinedMembers(\n roomId: string,\n asUserId: string,\n ): Promise<{ joined: Record<string, { display_name?: string }> }> {\n const url =\n `${this.homeserver}/_matrix/client/v3/rooms/${encodeURIComponent(roomId)}` +\n `/joined_members?user_id=${encodeURIComponent(asUserId)}`\n const r = await this.fetch(url, {\n method: 'GET',\n headers: { Authorization: `Bearer ${this.asToken}` },\n })\n if (!r.ok) throw new Error(`getJoinedMembers(${roomId}) failed: ${r.status}`)\n return (await r.json()) as { joined: Record<string, { display_name?: string }> }\n }\n\n async sync(opts: {\n asUserId: string\n since?: string | null\n timeoutMs?: number\n }): Promise<{\n next_batch: string\n rooms: {\n join: Record<string, {\n timeline: { events: Record<string, unknown>[]; prev_batch?: string; limited?: boolean }\n }>\n }\n }> {\n const params = new URLSearchParams({\n user_id: opts.asUserId,\n timeout: String(opts.timeoutMs ?? 30_000),\n })\n if (opts.since) params.set('since', opts.since)\n const url = `${this.homeserver}/_matrix/client/v3/sync?${params.toString()}`\n const r = await this.fetch(url, {\n headers: { Authorization: `Bearer ${this.asToken}` },\n })\n if (!r.ok) throw new Error(`sync(${opts.asUserId}) failed: ${r.status}`)\n return r.json() as Promise<{\n next_batch: string\n rooms: {\n join: Record<string, {\n timeline: { events: Record<string, unknown>[]; prev_batch?: string; limited?: boolean }\n }>\n }\n }>\n }\n\n async fetchRoomName(roomId: string, asUserId: string): Promise<string | null> {\n const url =\n `${this.homeserver}/_matrix/client/v3/rooms/${encodeURIComponent(roomId)}` +\n `/state/m.room.name/?user_id=${encodeURIComponent(asUserId)}`\n const r = await this.fetch(url, {\n method: 'GET',\n headers: { Authorization: `Bearer ${this.asToken}` },\n })\n if (r.status === 404) return null\n if (!r.ok) throw new Error(`fetchRoomName(${roomId}) failed: ${r.status}`)\n const body = (await r.json()) as { name?: string }\n return body.name ?? null\n }\n\n private async sendEvent(\n roomId: string,\n asUserId: string,\n eventType: string,\n content: Record<string, unknown>,\n ): Promise<{ event_id: string }> {\n const txn = randomUUID()\n const url =\n `${this.homeserver}/_matrix/client/v3/rooms/${encodeURIComponent(roomId)}` +\n `/send/${eventType}/${txn}?user_id=${encodeURIComponent(asUserId)}`\n const r = await this.fetch(url, {\n method: 'PUT',\n headers: { Authorization: `Bearer ${this.asToken}` },\n body: JSON.stringify(content),\n })\n if (!r.ok) throw new Error(`sendEvent(${eventType}) failed: ${r.status}`)\n return (await r.json()) as { event_id: string }\n }\n}\n","import type {\n TransportContextProvider,\n HistoryOptions,\n HistoryPage,\n Member,\n ChannelInfo,\n Message,\n ThreadOverview,\n ThreadOverviewPage,\n} from '@zooid/core'\nimport type { MatrixClient } from './matrix-client.js'\n\ninterface MatrixMessageEvent {\n event_id: string\n sender: string\n origin_server_ts: number\n type: string\n content?: {\n msgtype?: string\n body?: string\n 'm.relates_to'?: { rel_type?: string; event_id?: string }\n }\n unsigned?: {\n 'm.relations'?: {\n 'm.thread'?: {\n count?: number\n latest_event?: { origin_server_ts?: number }\n }\n }\n }\n}\n\nexport interface MatrixContextProviderOpts {\n client: MatrixClient\n /**\n * The **agent's own** Matrix user (`@{workstation}.{name}:server`), which\n * every read is impersonated as via `?user_id=`. Not the AS bot: that would\n * read every room on the homeserver and make this class the only thing\n * standing between an agent and someone else's conversation.\n *\n * This is load-bearing. It is what makes the homeserver — not our own\n * bookkeeping — the authorization boundary for context reads, so a room or\n * thread the agent is not in fails at Matrix with 403/404. Anything that\n * widens who can name a room (a CLI, a new tool parameter) is safe only\n * while this holds.\n */\n asUserId: string\n /** Map of Matrix user IDs → agent names, for is_agent / agent_name flags. */\n agentBots: Map<string, string>\n}\n\nexport class MatrixContextProvider implements TransportContextProvider {\n constructor(private readonly opts: MatrixContextProviderOpts) {}\n\n async getRoomHistory(channelId: string, hopts: HistoryOptions): Promise<HistoryPage> {\n // Server-side filter: only `m.room.message` events. Without this we'd\n // burn the page budget on reactions, `dev.zooid.*` custom events, typing\n // notifications, etc., and routinely return empty pages with a stale\n // `has_more` cursor.\n const { chunk, end } = await this.opts.client.fetchRoomMessages({\n roomId: channelId,\n asUserId: this.opts.asUserId,\n limit: hopts.limit,\n from: hopts.before,\n filter: { types: ['m.room.message'] },\n })\n const messages: Message[] = []\n for (let i = chunk.length - 1; i >= 0; i--) {\n const ev = chunk[i] as unknown as MatrixMessageEvent\n const msg = this.toMessage(ev)\n if (msg) messages.push(msg)\n }\n return {\n messages,\n next_before: end,\n has_more: end !== undefined,\n }\n }\n\n async getRecentThreads(\n channelId: string,\n hopts: HistoryOptions,\n ): Promise<ThreadOverviewPage> {\n // Server-side filter: `m.room.message` only, and exclude thread replies\n // (`not_rel_types: ['m.thread']`) so the overview shows top-level entries\n // and thread roots, not the reply noise underneath them.\n const { chunk, end } = await this.opts.client.fetchRoomMessages({\n roomId: channelId,\n asUserId: this.opts.asUserId,\n limit: hopts.limit,\n from: hopts.before,\n filter: { types: ['m.room.message'], not_rel_types: ['m.thread'] },\n })\n // /messages returns newest-first; keep that order for the overview.\n const threads: ThreadOverview[] = []\n for (const ev of chunk as unknown as MatrixMessageEvent[]) {\n if (ev.type !== 'm.room.message') continue\n // m.notice: agent prose sends as m.notice so\n // .m.rule.suppress_notices silences the chunk storm server-side\n // (ZNC025 §10) — a thread root sent by an agent must still surface here.\n if (\n (ev.content?.msgtype !== 'm.text' && ev.content?.msgtype !== 'm.notice') ||\n typeof ev.content.body !== 'string'\n )\n continue\n const relatesTo = ev.content['m.relates_to']\n if (relatesTo?.rel_type === 'm.thread') continue // skip thread replies\n const agent = this.opts.agentBots.get(ev.sender)\n const bundled = ev.unsigned?.['m.relations']?.['m.thread']\n const replyCount = bundled?.count ?? 0\n const latestTs = bundled?.latest_event?.origin_server_ts ?? ev.origin_server_ts\n threads.push({\n id: ev.event_id,\n sender: ev.sender,\n text: ev.content.body,\n timestamp: new Date(ev.origin_server_ts).toISOString(),\n is_agent: agent !== undefined,\n ...(agent !== undefined ? { agent_name: agent } : {}),\n reply_count: replyCount,\n last_activity_at: new Date(latestTs).toISOString(),\n })\n }\n return {\n threads,\n next_before: end,\n has_more: end !== undefined,\n }\n }\n\n async getThreadHistory(\n channelId: string,\n threadId: string,\n hopts: HistoryOptions,\n ): Promise<HistoryPage> {\n // Root event first (only on the first page when no pagination cursor).\n const messages: Message[] = []\n if (!hopts.before) {\n const root = (await this.opts.client.fetchEvent(\n channelId,\n threadId,\n this.opts.asUserId,\n )) as unknown as MatrixMessageEvent | null\n if (root) {\n const rootMsg = this.toMessage(root)\n if (rootMsg) messages.push({ ...rootMsg, thread_id: threadId })\n }\n }\n const { chunk, next_batch } = await this.opts.client.fetchThreadRelations({\n roomId: channelId,\n rootEventId: threadId,\n asUserId: this.opts.asUserId,\n limit: hopts.limit,\n from: hopts.before,\n })\n for (const ev of chunk as unknown as MatrixMessageEvent[]) {\n const reply = this.toMessage(ev)\n if (reply) messages.push({ ...reply, thread_id: threadId })\n }\n return {\n messages,\n next_before: next_batch,\n has_more: next_batch !== undefined,\n }\n }\n\n private toMessage(ev: MatrixMessageEvent): Message | null {\n if (ev.type !== 'm.room.message') return null\n const msgtype = ev.content?.msgtype\n const body = ev.content?.body\n const agent = this.opts.agentBots.get(ev.sender)\n const relatesTo = ev.content?.['m.relates_to']\n const threadId =\n relatesTo?.rel_type === 'm.thread' && relatesTo.event_id ? relatesTo.event_id : undefined\n\n // Media events render as context placeholders\n if (msgtype === 'm.image' || msgtype === 'm.file' || msgtype === 'm.video' || msgtype === 'm.audio') {\n const kind = msgtype.slice(2) // 'image', 'file', 'video', 'audio'\n const name = typeof body === 'string' && body ? body : 'untitled'\n return {\n id: ev.event_id,\n sender: ev.sender,\n text: `[${kind}: ${name}]`,\n timestamp: new Date(ev.origin_server_ts).toISOString(),\n is_agent: agent !== undefined,\n ...(agent !== undefined ? { agent_name: agent } : {}),\n ...(threadId !== undefined ? { thread_id: threadId } : {}),\n }\n }\n\n // Agent prose sends as m.notice (ZNC025 §10); m.text is human prose.\n if ((msgtype !== 'm.text' && msgtype !== 'm.notice') || typeof body !== 'string') return null\n return {\n id: ev.event_id,\n sender: ev.sender,\n text: body,\n timestamp: new Date(ev.origin_server_ts).toISOString(),\n is_agent: agent !== undefined,\n ...(agent !== undefined ? { agent_name: agent } : {}),\n ...(threadId !== undefined ? { thread_id: threadId } : {}),\n }\n }\n\n async getChannelMembers(channelId: string): Promise<Member[]> {\n const { joined } = await this.opts.client.getJoinedMembers(channelId, this.opts.asUserId)\n return Object.entries(joined).map(([id, info]) => {\n const agent = this.opts.agentBots.get(id)\n return {\n id,\n name: info.display_name ?? id,\n is_agent: agent !== undefined,\n ...(agent !== undefined ? { agent_name: agent } : {}),\n }\n })\n }\n\n async getChannelInfo(channelId: string): Promise<ChannelInfo> {\n const name = await this.opts.client.fetchRoomName(channelId, this.opts.asUserId)\n return {\n id: channelId,\n name: name ?? channelId,\n transport: 'matrix',\n }\n }\n}\n","import { stringify } from 'yaml'\n\nexport interface MatrixTransportConfig {\n id: string\n url: string\n homeserver: string\n asToken: string\n hsToken: string\n senderLocalpart: string\n /** Regex covering all bot users, e.g. `@.*:example.com` */\n userNamespace: string\n /**\n * Optional regex covering aliases the AS may claim, e.g. `#.*:example.com`.\n * Required when the AS calls `createRoom` with a `room_alias_name`.\n */\n aliasNamespace?: string\n /**\n * Whether the AS exclusively owns the user_namespace. Default true.\n * Set false when humans share the namespace (e.g., `zooid dev` registers\n * a predefined admin under the same `@.*:localhost` regex).\n */\n exclusive?: boolean\n}\n\nexport function renderRegistration(c: MatrixTransportConfig): string {\n return stringify(\n {\n id: c.id,\n url: c.url,\n as_token: c.asToken,\n hs_token: c.hsToken,\n sender_localpart: c.senderLocalpart,\n rate_limited: false,\n namespaces: {\n users: [{ exclusive: c.exclusive ?? true, regex: c.userNamespace }],\n aliases: c.aliasNamespace\n ? [{ exclusive: c.exclusive ?? true, regex: c.aliasNamespace }]\n : [],\n rooms: [],\n },\n },\n { defaultStringType: 'PLAIN', defaultKeyType: 'PLAIN', singleQuote: true },\n )\n}\n","// A workstation id (and an agent key) must be \"slug-shaped\": kebab-case,\n// MXID/alias-safe. SLUG_RE names the *format*; the domain noun is workstation.\nexport const SLUG_RE = /^[a-z0-9-]+$/\nexport const AGENT_KEY_RE = /^[a-z0-9-]+$/\n\nexport const isValidWorkstation = (s: string): boolean => SLUG_RE.test(s)\nexport const isValidAgentKey = (s: string): boolean => AGENT_KEY_RE.test(s)\n\nexport function agentMxid(workstation: string, agent: string, serverName: string): string {\n if (!isValidWorkstation(workstation))\n throw new Error(`invalid workstation: ${JSON.stringify(workstation)}`)\n if (!isValidAgentKey(agent)) throw new Error(`invalid agent key: ${JSON.stringify(agent)}`)\n return `@${workstation}.${agent}:${serverName}`\n}\n\nexport function splitAgentLocalpart(localpart: string): { workstation: string; agent: string } {\n const i = localpart.indexOf('.') // first dot is the boundary; workstation/agent are dot-free\n if (i <= 0 || i === localpart.length - 1)\n throw new Error(`not a workstation.agent localpart: ${localpart}`)\n return { workstation: localpart.slice(0, i), agent: localpart.slice(i + 1) }\n}\n\nexport function workstationUserNamespace(workstation: string, serverName: string): string {\n if (!isValidWorkstation(workstation))\n throw new Error(`invalid workstation: ${JSON.stringify(workstation)}`)\n return `@${workstation}\\\\..*:${serverName}` // escaped dot — exclusive to this workstation's agents\n}\n","export interface MaybeMessage {\n content?: {\n 'm.mentions'?: { user_ids?: string[] }\n body?: string\n formatted_body?: string\n }\n}\n\nconst MATRIX_TO_RE = /https:\\/\\/matrix\\.to\\/#\\/(@[^\"<>\\s]+)/g\nconst RAW_USER_RE = /(@[A-Za-z0-9._\\-=/+]+:[A-Za-z0-9.\\-]+)/g\n\nexport function extractMentions(event: MaybeMessage): string[] {\n const out = new Set<string>()\n const c = event.content ?? {}\n for (const id of c['m.mentions']?.user_ids ?? []) out.add(id)\n if (c.formatted_body) {\n for (const m of c.formatted_body.matchAll(MATRIX_TO_RE)) {\n out.add(decodeURIComponent(m[1]))\n }\n }\n if (c.body && out.size === 0) {\n for (const m of c.body.matchAll(RAW_USER_RE)) out.add(m[1])\n }\n return [...out]\n}\n\n/**\n * Remove a single user-id mention from a message body. Strips the raw\n * `@local:server` form and collapses any whitespace that the strip leaves\n * behind. Other users' mentions are preserved verbatim so the agent can\n * reason about them.\n */\nexport function stripMention(body: string, userId: string): string {\n const escaped = userId.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')\n // Match optional surrounding whitespace so we don't leave double spaces.\n const re = new RegExp(`\\\\s*${escaped}\\\\s*`, 'g')\n return body.replace(re, ' ').replace(/\\s+/g, ' ').trim()\n}\n","import type { RoomBinding } from '@zooid/core'\nimport { extractMentions } from './mentions.js'\n\nexport type { RoomBinding }\n\nexport interface AgentBinding {\n name: string\n userId: string\n /** Optional human-readable display name. Falls back to the user_id localpart. */\n displayName?: string\n /**\n * Rooms this agent is bound to. Each entry's `alias` starts out as the\n * configured `#alias` (or `!id`) and is rewritten to the canonical room\n * ID by `BotPool.bootstrap`. Optional `powerLevel` is seeded into the\n * room's `m.room.power_levels.users` at room creation only.\n */\n rooms: RoomBinding[]\n trigger: 'mention' | 'any'\n /** Host path of the agent's workspace (resolved agent.workdir). Media files land here. */\n workspaceDir?: string\n /** Path prefix as the agent sees it: '/workspace' for containers, = workspaceDir for local. */\n agentWorkspacePath?: string\n}\n\nexport const MEDIA_MSGTYPES = new Set(['m.image', 'm.file', 'm.video', 'm.audio'])\n\nexport function isMediaMsgtype(t: string | undefined): boolean {\n return t !== undefined && MEDIA_MSGTYPES.has(t)\n}\n\nexport interface ThreadState {\n /** Agent names that have posted in this thread, in order. */\n participants: string[]\n /** Agent names @mentioned in the thread root event (or subsequently). */\n rootMentions: string[]\n /**\n * Agent-to-agent call edges: sub-agent name → the agent that @mentioned\n * (called) it in this thread. A sub's bare reply bubbles up to its caller;\n * a caller never implicitly re-triggers its callee. Makes agent↔agent\n * acknowledgement loops structurally impossible. See [[ZOD039]] §\n * Implicit triggers → Directional continuation.\n */\n callers: Record<string, string>\n /**\n * Handoff arcs per sub-agent: the event ids of the agent→agent @mention\n * messages that called it, in timeline order (last = current arc). Each\n * call opens a fresh ACP session keyed `threadRoot|callEventId`\n * ([[ZOD071]]). Append-only; rebuilt from the timeline after a restart.\n */\n handoffs: Record<string, string[]>\n}\n\ninterface MaybeEvent {\n type?: string\n room_id?: string\n sender?: string\n content?: {\n msgtype?: string\n 'm.relates_to'?: { rel_type?: string; event_id?: string }\n }\n}\n\nexport type RouteMatch = AgentBinding\n\nfunction inboundThreadRoot(event: MaybeEvent): string | undefined {\n const r = event.content?.['m.relates_to']\n return r?.rel_type === 'm.thread' && r.event_id ? r.event_id : undefined\n}\n\nexport function route(\n event: MaybeEvent,\n agents: AgentBinding[],\n threadStates?: Map<string, ThreadState>,\n): RouteMatch[] {\n if (event.type !== 'm.room.message') return []\n if (!event.content?.msgtype) return []\n if (isMediaMsgtype(event.content.msgtype)) return []\n const mentions = new Set(extractMentions(event as never))\n const matches: RouteMatch[] = []\n const threadRoot = inboundThreadRoot(event)\n const threadState = threadRoot ? threadStates?.get(threadRoot) : undefined\n\n for (const a of agents) {\n if (event.sender === a.userId) continue\n if (!a.rooms.some((r) => r.alias === event.room_id)) continue\n if (a.trigger === 'any') {\n matches.push(a)\n continue\n }\n // trigger === 'mention'\n if (mentions.has(a.userId)) {\n matches.push(a)\n continue\n }\n // Implicit trigger in a thread.\n if (threadState) {\n const senderAgent = agents.find((x) => x.userId === event.sender)\n if (senderAgent) {\n // Agent reply = a \"return\": route only to the agent that called the\n // sender (its caller), never to a callee. Directional continuation\n // keeps agent↔agent handoffs from looping — the call graph is a tree\n // rooted at the human, so returns only ever walk up.\n if (threadState.callers[senderAgent.name] === a.name) matches.push(a)\n } else {\n // Human (or non-agent) follow-up: continue with the most-recent-posting\n // agent, or inherit the root mention if no agent has posted yet.\n const lastPoster = threadState.participants.at(-1)\n if (lastPoster) {\n if (lastPoster === a.name) matches.push(a)\n } else if (threadState.rootMentions.includes(a.name)) {\n matches.push(a)\n }\n }\n }\n }\n return matches\n}\n","import { MatrixClient } from './matrix-client.js'\n\nexport interface EnsureSpaceOpts {\n client: MatrixClient\n asUserId: string\n serverName: string\n spaceLocalpart: string\n preset: 'public_chat' | 'private_chat'\n /**\n * Operator MXIDs to seed at PL 100 in the space's `m.room.power_levels`\n * at creation. The AS bot is always included. Empty/absent → no override\n * (the preset's PL defaults apply). Only consulted on first creation —\n * if the alias already resolves we return the existing room untouched.\n */\n admins?: string[]\n /**\n * Join rule pinned on the space at creation. Defaults to `invite`: a\n * workspace is joined by invitation, not self-service, so it can't be walked\n * into (which would otherwise satisfy every restricted child room's `allow`).\n * `zooid dev` passes `public` so a self-service-registered local account can\n * join `#<space>` straight from the web client without an invite — acceptable\n * because the dev homeserver is local-only and never deployed.\n */\n joinRule?: 'invite' | 'public'\n}\n\nexport async function ensureWorkforceSpace(opts: EnsureSpaceOpts): Promise<string> {\n const alias = `#${opts.spaceLocalpart}:${opts.serverName}`\n const existing = await opts.client.resolveAlias(alias)\n if (existing) return existing\n\n const display = opts.spaceLocalpart.charAt(0).toUpperCase() + opts.spaceLocalpart.slice(1)\n const body: Record<string, unknown> = {\n room_alias_name: opts.spaceLocalpart,\n name: display,\n preset: opts.preset,\n creation_content: { type: 'm.space' },\n // Pin the join rule regardless of preset. Defaults to invite so the space\n // can't be walked into (which would otherwise satisfy every restricted\n // child room's allow); `zooid dev` opts into `public` for local-only use.\n initial_state: [\n { type: 'm.room.join_rules', state_key: '', content: { join_rule: opts.joinRule ?? 'invite' } },\n ],\n }\n if (opts.admins && opts.admins.length > 0) {\n // Invite each admin so they actually become members — PL 100 alone does\n // not grant membership in an invite-only space.\n body.invite = opts.admins\n const users: Record<string, number> = { [opts.asUserId]: 100 }\n for (const a of opts.admins) users[a] = 100\n body.power_level_content_override = { users }\n }\n return opts.client.createRoomRaw({ asUserId: opts.asUserId, body })\n}\n\nexport interface EnsureDefaultChannelOpts {\n client: MatrixClient\n asUserId: string\n serverName: string\n spaceId: string\n /** Localpart of the default channel; defaults to `general`. */\n channelLocalpart?: string\n /**\n * Operator MXIDs to seed at PL 100 in the channel's `m.room.power_levels`\n * at creation. The AS bot is always included. Empty/absent → no override\n * (the preset's PL defaults apply). Only consulted on first creation —\n * if the alias already resolves we return the existing room untouched.\n */\n admins?: string[]\n}\n\n/**\n * Ensure a space has a default channel (`#general` by default), restricted to\n * the space's members and attached as an `m.space.child`. Idempotent: returns\n * the existing room if the alias already resolves. Has no agent — it's the\n * human landing room, so it is created here at provisioning time rather than\n * via the agent-room path.\n */\nexport async function ensureDefaultChannel(opts: EnsureDefaultChannelOpts): Promise<string> {\n const localpart = opts.channelLocalpart ?? 'general'\n const alias = `#${localpart}:${opts.serverName}`\n const existing = await opts.client.resolveAlias(alias)\n if (existing) return existing\n\n let userPowerLevels: Record<string, number> | undefined\n if (opts.admins && opts.admins.length > 0) {\n userPowerLevels = { [opts.asUserId]: 100 }\n for (const a of opts.admins) userPowerLevels[a] = 100\n }\n\n const roomId = await opts.client.createRoom({\n roomAliasName: localpart,\n invite: [],\n senderUserId: opts.asUserId,\n name: localpart.charAt(0).toUpperCase() + localpart.slice(1),\n restrictedToSpaceId: opts.spaceId,\n ...(userPowerLevels ? { userPowerLevels } : {}),\n })\n await opts.client.sendStateEvent({\n roomId: opts.spaceId,\n asUserId: opts.asUserId,\n eventType: 'm.space.child',\n stateKey: roomId,\n content: { via: [opts.serverName] },\n })\n return roomId\n}\n\nexport function serverNameFromMxid(mxid: string): string {\n const colon = mxid.indexOf(':')\n if (colon < 0) {\n throw new Error(`mxid lacks server: ${mxid}`)\n }\n return mxid.slice(colon + 1)\n}\n","import type { MatrixClient } from './matrix-client.js'\nimport type { AgentBinding } from './router.js'\nimport { serverNameFromMxid } from './space-provisioner.js'\n\nexport interface BootstrapOpts {\n /** Invited to any newly-created room; absent = no invite. */\n adminUserId?: string\n /** Workforce space room ID. When set, every resolved agent room is attached as m.space.child. */\n spaceRoomId?: string\n /** AS bot user ID. Required when spaceRoomId is set; sender of the m.space.child write. */\n asUserId?: string\n /**\n * Operator MXIDs seeded at PL 100 in every agent room this pool creates.\n * Applied via `power_level_content_override.users` at room creation only —\n * never reconciled. Empty/absent = no operator entries.\n */\n adminUserIds?: string[]\n}\n\nexport class BotPool {\n constructor(\n private readonly client: Pick<\n MatrixClient,\n | 'registerBot'\n | 'invite'\n | 'joinRoom'\n | 'resolveAlias'\n | 'createRoom'\n | 'sendStateEvent'\n | 'setDisplayName'\n >,\n private readonly agents: AgentBinding[],\n ) {}\n\n async bootstrap(opts: BootstrapOpts = {}): Promise<void> {\n const aliasToId = new Map<string, string>()\n const attachedToSpace = new Set<string>()\n for (const a of this.agents) {\n const lp = localpart(a.userId)\n try {\n await this.client.registerBot(lp)\n } catch (err) {\n console.warn(`[matrix] register failed for ${a.userId}: ${(err as Error).message}`)\n }\n try {\n await this.client.setDisplayName(a.userId, a.displayName ?? lp)\n } catch (err) {\n console.warn(`[matrix] setDisplayName(${a.userId}) failed: ${(err as Error).message}`)\n }\n // Make each agent a member of the workforce space. That covers two\n // things at once: the dev.zooid.workforce roster is now backed by\n // actual space membership (so the Zoon client's member autocomplete\n // works across rooms), and every restricted child room's allow rule\n // is satisfied without per-room invites.\n if (opts.spaceRoomId && opts.asUserId) {\n try {\n await this.client.invite({\n roomId: opts.spaceRoomId,\n asUserId: opts.asUserId,\n targetUserId: a.userId,\n })\n await this.client.joinRoom(opts.spaceRoomId, a.userId)\n } catch (err) {\n console.warn(\n `[matrix] space membership for ${a.userId} failed: ${(err as Error).message}`,\n )\n }\n }\n for (let i = 0; i < a.rooms.length; i++) {\n const binding = a.rooms[i]!\n const room = binding.alias\n try {\n let resolved = room\n if (room.startsWith('#')) {\n const cached = aliasToId.get(room)\n if (cached) {\n resolved = cached\n } else {\n const existing = await this.client.resolveAlias(room)\n if (existing) {\n resolved = existing\n } else {\n const colon = room.indexOf(':')\n const aliasLocalpart = colon > 1 ? room.slice(1, colon) : room.slice(1)\n // Workstation-created rooms are owned by the AS sender bot (PL\n // 100, already seeded in the power override below). Agents join\n // as plain members and must never be room admins. The human\n // operator is invited + seeded at PL 100, but can't be the\n // creator under a workstation-scoped namespace — the AS token\n // can't impersonate a non-namespaced human.\n const sender = opts.asUserId ?? opts.adminUserId ?? a.userId\n const userPowerLevels = buildUserPowerLevels(\n opts.asUserId,\n opts.adminUserIds,\n this.agents,\n room,\n )\n try {\n resolved = await this.client.createRoom({\n roomAliasName: aliasLocalpart,\n invite: opts.adminUserId ? [opts.adminUserId] : [],\n senderUserId: sender,\n name: aliasLocalpart,\n ...(opts.spaceRoomId ? { restrictedToSpaceId: opts.spaceRoomId } : {}),\n ...(userPowerLevels ? { userPowerLevels } : {}),\n })\n } catch (err) {\n // Another agent — or another daemon process bootstrapping the\n // same workforce — may have created this room concurrently, so\n // its alias is now taken (createRoom fails with the alias in\n // use). Re-resolve and join the existing room rather than\n // leaving an orphan. Only rethrow if the alias still doesn't\n // resolve (a genuine creation failure).\n const raced = await this.client.resolveAlias(room)\n if (!raced) throw err\n resolved = raced\n }\n }\n aliasToId.set(room, resolved)\n }\n }\n // Rewrite the binding's alias to the canonical room ID so the\n // router (which matches on event.room_id) sees a hit when Tuwunel\n // pushes events. The declared powerLevel was a creation-time\n // seed — it has no role after bootstrap.\n binding.alias = resolved\n await this.client.joinRoom(resolved, a.userId)\n\n if (\n opts.spaceRoomId &&\n opts.asUserId &&\n !attachedToSpace.has(resolved)\n ) {\n attachedToSpace.add(resolved)\n const via = serverNameFromMxid(a.userId)\n try {\n await this.client.sendStateEvent({\n roomId: opts.spaceRoomId,\n asUserId: opts.asUserId,\n eventType: 'm.space.child',\n stateKey: resolved,\n content: { via: [via] },\n })\n } catch (err) {\n console.warn(\n `[matrix] m.space.child(${resolved}) failed: ${(err as Error).message}`,\n )\n }\n }\n } catch (err) {\n console.warn(\n `[matrix] join failed (${a.userId} → ${room}): ${(err as Error).message}`,\n )\n }\n }\n }\n }\n\n findByUserId(userId: string): AgentBinding | undefined {\n return this.agents.find((a) => a.userId === userId)\n }\n\n findByName(name: string): AgentBinding | undefined {\n return this.agents.find((a) => a.name === name)\n }\n}\n\nfunction localpart(userId: string): string {\n const m = /^@([^:]+):/.exec(userId)\n if (!m) throw new Error(`bad user id: ${userId}`)\n return m[1]\n}\n\n/**\n * Build the `userPowerLevels` map for a room about to be created. Always\n * seeds the AS bot (when known) and any operator admins at PL 100; layers\n * per-agent declared PLs on top so agents land at moderator (or whatever\n * they declared) without a follow-up state write. Returns undefined when\n * the map would be empty — `createRoom` then omits the override entirely\n * and the preset's defaults apply.\n */\nfunction buildUserPowerLevels(\n asUserId: string | undefined,\n admins: string[] | undefined,\n agents: AgentBinding[],\n roomAlias: string,\n): Record<string, number> | undefined {\n const users: Record<string, number> = {}\n if (asUserId) users[asUserId] = 100\n if (admins) for (const a of admins) users[a] = 100\n for (const a of agents) {\n for (const r of a.rooms) {\n if (r.alias !== roomAlias) continue\n if (r.powerLevel !== undefined) users[a.userId] = r.powerLevel\n }\n }\n return Object.keys(users).length > 0 ? users : undefined\n}\n","import { Hono } from 'hono'\nimport { timingSafeEqual } from 'node:crypto'\nimport type { AcpRegistry, ApprovalCorrelator, RegisteredApproval } from '@zooid/core'\nimport type { AgentEvent, ContentBlock } from '@zooid/acp-client'\nimport { MatrixClient } from './matrix-client.js'\nimport { BotPool } from './bot-pool.js'\nimport { route, isMediaMsgtype, type AgentBinding, type ThreadState } from './router.js'\nimport { sessionKeyFor, composeHandoffKey } from './session-keys.js'\nimport { stripMention, extractMentions } from './mentions.js'\nimport {\n toToolCallBody,\n toUpdateBody,\n toPlanBody,\n toAvailableCommandsBody,\n toErrorBody,\n toTurnEndBody,\n} from './event-encoders.js'\nimport { classify } from '@zooid/acp-client'\nimport { toMatrixHtml } from './markdown-to-matrix-html.js'\nimport {\n PendingMediaStore,\n type PendingMediaItem,\n} from './pending-media.js'\nimport {\n MediaClient,\n MAX_INLINE_IMAGE_BYTES,\n INLINE_IMAGE_MIMES,\n} from './media-client.js'\nimport { writeAttachment } from './attachments.js'\nimport { SyncLoop } from './sync-loop.js'\n\nexport interface MediaClientLike {\n download(input: {\n mxcUri: string\n asUserId: string\n maxBytes?: number\n }): Promise<{ data: Uint8Array; contentType: string }>\n upload(input: {\n data: Uint8Array\n contentType: string\n filename?: string\n asUserId: string\n }): Promise<{ content_uri: string }>\n}\n\nexport interface CreateMatrixTransportOptions {\n agents: AcpRegistry\n approvals: ApprovalCorrelator\n client: MatrixClient\n bindings: AgentBinding[]\n hsToken: string\n /** Admin Matrix user ID. When set, BotPool.bootstrap invites this user into rooms it creates. */\n adminUserId?: string\n /** Post-turn drain: keep collecting trailing `agent_message_chunk`s until the\n * buffer is quiet for this long before flushing. Defaults to `DRAIN_QUIET_MS`.\n * Set to 0 to disable the drain (e.g. in tests). */\n drainQuietMs?: number\n /** Hard cap on the post-turn drain. Defaults to `DRAIN_MAX_MS`. */\n drainMaxMs?: number\n /** Injected media client for downloading/uploading Matrix media. */\n media?: MediaClientLike\n /** Injected attachment writer (defaults to the real writeAttachment). */\n writeAttachmentFn?: typeof writeAttachment\n /** AS sender-bot MXID (@<sender_localpart>:<server>). Together with the agent\n * bindings this forms the set of \"our bot users\" whose ad-hoc invites are\n * declined. */\n botUserId?: string\n /**\n * Transport ingestion mode.\n * - `'appservice'` (default): Tuwunel pushes events to the HTTP transaction endpoint.\n * - `'client'`: daemon polls via impersonated `/sync` per agent (pull mode).\n */\n mode?: 'appservice' | 'client'\n /** Pull mode: load the persisted `since` cursor for an agent user ID. */\n loadSince?: (agentUserId: string) => string | null\n /** Pull mode: persist the `since` cursor after each sync poll. */\n saveSince?: (agentUserId: string, since: string) => void\n}\n\ninterface SessionContext {\n agent: AgentBinding\n roomId: string\n /** Always set — every session is thread-scoped via agent-promotion. */\n threadRoot: string\n}\n\ninterface MatrixEvent {\n type?: string\n event_id?: string\n origin_server_ts?: number\n room_id?: string\n sender?: string\n /** Present on state events (m.room.member → the affected user). */\n state_key?: string\n content?: Record<string, unknown> & {\n msgtype?: string\n body?: string\n membership?: string\n 'm.relates_to'?: { rel_type?: string; event_id?: string }\n }\n}\n\nconst STARTUP_GRACE_MS = 5_000\n\ninterface MediaBlocksResult {\n blocks: ContentBlock[]\n pathLines: string[]\n}\n\nasync function buildMediaBlocks(\n items: PendingMediaItem[],\n opts: {\n agent: AgentBinding\n media: MediaClientLike | undefined\n writeAttachmentFn: typeof writeAttachment\n onError: (item: PendingMediaItem, err: unknown) => void\n },\n): Promise<MediaBlocksResult> {\n const blocks: ContentBlock[] = []\n const pathLines: string[] = []\n\n if (!opts.media || items.length === 0) return { blocks, pathLines }\n\n for (const item of items) {\n try {\n const isInlineCandidate =\n item.msgtype === 'm.image' &&\n INLINE_IMAGE_MIMES.includes(item.info?.mimetype ?? '') &&\n (item.info?.size === undefined || item.info.size <= MAX_INLINE_IMAGE_BYTES)\n\n if (isInlineCandidate) {\n const { data, contentType } = await opts.media.download({\n mxcUri: item.url,\n asUserId: opts.agent.userId,\n })\n // Double-check actual size (info can lie)\n if (data.byteLength <= MAX_INLINE_IMAGE_BYTES) {\n blocks.push({\n type: 'image',\n data: Buffer.from(data).toString('base64'),\n mimeType: contentType,\n })\n continue\n }\n // Actual size exceeded cap — fall through to file route with the already-downloaded bytes\n if (opts.agent.workspaceDir) {\n const paths = opts.writeAttachmentFn({\n workspaceDir: opts.agent.workspaceDir,\n agentWorkspacePath: opts.agent.agentWorkspacePath ?? opts.agent.workspaceDir,\n eventId: item.eventId,\n filename: item.filename ?? item.body,\n data,\n })\n blocks.push({\n type: 'resource_link',\n uri: `file://${paths.agentPath}`,\n name: item.filename ?? item.body,\n })\n pathLines.push(`Attached file: ${paths.agentPath}`)\n }\n } else {\n // File route (m.file, m.video, m.audio, or oversized image)\n if (!opts.agent.workspaceDir) continue\n const { data } = await opts.media.download({\n mxcUri: item.url,\n asUserId: opts.agent.userId,\n })\n const paths = opts.writeAttachmentFn({\n workspaceDir: opts.agent.workspaceDir,\n agentWorkspacePath: opts.agent.agentWorkspacePath ?? opts.agent.workspaceDir,\n eventId: item.eventId,\n filename: item.filename ?? item.body,\n data,\n })\n blocks.push({\n type: 'resource_link',\n uri: `file://${paths.agentPath}`,\n name: item.filename ?? item.body,\n mimeType: item.info?.mimetype,\n size: item.info?.size,\n })\n pathLines.push(`Attached file: ${paths.agentPath}`)\n }\n } catch (err) {\n opts.onError(item, err)\n }\n }\n\n return { blocks, pathLines }\n}\n\nasync function sendMediaError(\n ctx: { agent: AgentBinding; roomId: string; threadRoot: string },\n _err: unknown,\n message: string,\n client: MatrixClient,\n): Promise<void> {\n await client\n .sendCustomEvent({\n roomId: ctx.roomId,\n asUserId: ctx.agent.userId,\n eventType: 'dev.zooid.error',\n content: toErrorBody(\n {\n kind: 'error' as const,\n agentId: ctx.agent.name,\n sessionId: null,\n turnId: null,\n code: 'media_failed',\n message: message.slice(0, 250),\n transient: false,\n },\n ctx.threadRoot,\n ),\n })\n .catch((e) => console.warn(`[matrix:${ctx.agent.name}] dev.zooid.error send failed:`, e))\n}\nconst SEEN_EVENT_CAP = 5_000\n\n// ACP only guarantees that an agent flushes pending `session/update`\n// notifications before the `session/prompt` response in the *cancellation*\n// path; for a normal turn the ordering is unspecified. Some agents (e.g.\n// opencode) emit trailing `agent_message_chunk`s a few ms after the stopReason\n// response, so finalizing the moment `prompt()` resolves truncates the reply.\n// After the turn resolves we wait for the buffer to stay unchanged for\n// DRAIN_QUIET_MS (debounce — re-arms on each late chunk) before flushing,\n// capped at DRAIN_MAX_MS so a misbehaving stream can't hang the turn.\nconst DRAIN_QUIET_MS = 300\n// 30s upper bound on how long we wait after `session/prompt` resolves before\n// flushing whatever we have (or declaring an empty turn). Set high because\n// some agents — opencode especially — resolve the prompt promise *before*\n// the agent_message_chunk stream starts, and the chunk burst can be 5–15s\n// after that. The drain still short-circuits via DRAIN_QUIET_MS once any\n// content has settled, so this cap only kicks in for genuinely-stuck turns.\nconst DRAIN_MAX_MS = 30_000\n\nconst delay = (ms: number): Promise<void> => new Promise((r) => setTimeout(r, ms))\n\nfunction inboundThreadRoot(evt: MatrixEvent): string | undefined {\n const r = evt.content?.['m.relates_to']\n return r?.rel_type === 'm.thread' && r.event_id ? r.event_id : undefined\n}\n\nexport function createMatrixTransport(opts: CreateMatrixTransportOptions) {\n const { agents, approvals, client, bindings, hsToken, adminUserId, botUserId, mode = 'appservice' } = opts\n const drainQuietMs = opts.drainQuietMs ?? DRAIN_QUIET_MS\n const drainMaxMs = opts.drainMaxMs ?? DRAIN_MAX_MS\n const mediaClient = opts.media\n const writeAttachmentFn = opts.writeAttachmentFn ?? writeAttachment\n const pendingMedia = new PendingMediaStore()\n const pool = new BotPool(client, bindings)\n const ourBotUserIds = new Set<string>([\n ...(botUserId ? [botUserId] : []),\n ...bindings.map((b) => b.userId),\n ])\n const DECLINE_REASON =\n 'Bots are placed in rooms only by the zooid daemon (workforce-as-code). ' +\n 'Ad-hoc invites are declined — add the bot to the room in zooid.yaml.'\n const sessions = new Map<string, SessionContext>()\n const buffers = new Map<string, string>()\n // Last messageId seen per session's buffer. opencode streams each assistant\n // message under its own id with no delimiter chunk between them, so a change\n // here marks a message boundary we must break on.\n const bufferMessageIds = new Map<string, string>()\n // Per-session promise tail so out-of-band events (tool_call, plan, etc.)\n // serialize on the wire even though the ACP producer doesn't await us.\n const sendQueue = new Map<string, Promise<void>>()\n // Thread participation index: keyed by thread root event_id.\n const threadStates = new Map<string, ThreadState>()\n // Drop events older than this — in push (appservice) mode Tuwunel may replay\n // a backlog after the daemon was offline, and we don't want yesterday's\n // \"@docs hi\" to fire now. In pull (client) mode the persisted `since` cursor\n // is the authoritative replay boundary (process everything after it — that's\n // exactly the offline-resume feature), so the timestamp guard must NOT apply:\n // the missed-while-offline mention is older than startup by design.\n const cutoffTs = mode === 'client' ? Number.NEGATIVE_INFINITY : Date.now() - STARTUP_GRACE_MS\n // Idempotency: appservice transactions are retried on 4xx/5xx/timeout, and\n // the same event_id can arrive twice. Skip ones we've already taken.\n const seenEventIds = new Set<string>()\n // Messages flushed per session this turn. Lets the drain loop tell \"stream\n // not started yet\" (0 flushes, empty buffer → keep waiting) from \"turn done,\n // last message already flushed mid-stream\" (>0 flushes, empty buffer → stop).\n const flushedCounts = new Map<string, number>()\n // Commands a shim advertises during session load/new — i.e. before runTurn\n // registers the session ctx (sessions.set). Stashed here keyed by sessionId\n // and replayed once the ctx exists, so `available_commands_update` (which is\n // only ever emitted at session establishment, never mid-turn) isn't dropped.\n const pendingCommands = new Map<string, AgentEvent>()\n\n // Build the m.text content for a chunk of assistant prose, attaching a\n // formatted_body only when the HTML render adds rich text the plain body\n // can't carry (marked wraps plain prose in <p>…</p>; skip that — most\n // clients render `body` better than a stripped re-encode).\n const buildTextContent = (\n text: string,\n ): { msgtype: string; body: string; [k: string]: unknown } => {\n const content: { msgtype: string; body: string; [k: string]: unknown } = {\n // m.notice, not m.text: .m.rule.suppress_notices silences the\n // chunk-storm of agent prose server-side (ZNC025 §10) instead of every\n // client having to filter it. dev.zooid.error carries the same tweak\n // for the same reason.\n msgtype: 'm.notice',\n body: text,\n }\n const html = toMatrixHtml(text)\n if (html) {\n const escapedPlain =\n '<p>' +\n text.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;') +\n '</p>'\n const norm = (s: string) => s.replace(/\\s+/g, ' ').trim()\n if (norm(html) !== norm(escapedPlain)) {\n content.format = 'org.matrix.custom.html'\n content.formatted_body = html\n }\n }\n return content\n }\n\n // Flush a session's buffered assistant text as its own Matrix message and\n // clear the buffer. No-op on an empty buffer. The send is chained onto\n // sendQueue so it orders correctly against tool_call/plan events from the\n // same turn. The buffer is cleared synchronously (before the first await),\n // so a chunk for the *next* message that arrives during the send starts\n // fresh. Returns true when a message was enqueued.\n const lastFlushed = new Map<string, string>()\n\n const flushBuffer = (sessionId: string): boolean => {\n const ctx = sessions.get(sessionId)\n const text = buffers.get(sessionId) ?? ''\n if (!ctx || text.length === 0) return false\n buffers.set(sessionId, '')\n // Kept for turn.end's push preview: the prose goes out as `m.notice` and\n // is deliberately silenced server-side, so turn.end is the only event that\n // can tell the user what the agent actually said.\n lastFlushed.set(sessionId, text)\n flushedCounts.set(sessionId, (flushedCounts.get(sessionId) ?? 0) + 1)\n const content = buildTextContent(text)\n const tail = (sendQueue.get(sessionId) ?? Promise.resolve()).then(async () => {\n try {\n await client.sendMessage({\n roomId: ctx.roomId,\n asUserId: ctx.agent.userId,\n content,\n threadRoot: ctx.threadRoot,\n })\n } catch (err) {\n console.warn(`[matrix:${ctx.agent.name}] sendMessage flush failed:`, err)\n }\n })\n sendQueue.set(sessionId, tail)\n return true\n }\n\n agents.onEvent = async (name, event: AgentEvent) => {\n const ctx = sessions.get(event.sessionId)\n if (!ctx) {\n // available_commands_update is advertised during ensureSession (session\n // load/new), before runTurn calls sessions.set — so the ctx isn't there\n // yet. Stash the latest roster and replay it once runTurn registers the\n // ctx. Other event types arriving without a ctx are genuinely orphaned\n // (e.g. replayed history for a thread we're not handling) — drop them.\n if (event.type === 'available_commands') {\n pendingCommands.set(event.sessionId, event)\n } else {\n console.warn(`[matrix:${name}] no session ctx for ${event.sessionId}`)\n }\n return\n }\n\n if (event.type === 'agent_message_chunk') {\n const block = event.content as { type?: string; text?: string; data?: string; mimeType?: string }\n if (block.type === 'text' && typeof block.text === 'string') {\n // A change in ACP messageId marks the previous assistant message as\n // complete. opencode streams each assistant message under its own id\n // with no delimiter chunk between them, so a change here is the only\n // boundary signal. Flush the previous message as its own Matrix\n // message — each ACP message lands separately (and interleaves with\n // tool_call/plan events) instead of welding into one turn-end blob.\n const prevMessageId = bufferMessageIds.get(event.sessionId)\n const messageChanged =\n event.messageId !== undefined &&\n prevMessageId !== undefined &&\n event.messageId !== prevMessageId\n if (event.messageId !== undefined)\n bufferMessageIds.set(event.sessionId, event.messageId)\n // flushBuffer clears the buffer synchronously, so the new message's\n // text below starts fresh.\n if (messageChanged) flushBuffer(event.sessionId)\n // Within a single message, tokens carry their own leading spaces, so we\n // concatenate raw. An empty chunk (some agents emit one between blocks,\n // e.g. after a tool call within the same message) is a paragraph break.\n const current = buffers.get(event.sessionId) ?? ''\n const needsBreak = current.length > 0 && block.text === ''\n const prefix = needsBreak ? '\\n\\n' : ''\n buffers.set(event.sessionId, current + prefix + block.text)\n } else if (\n block.type === 'image' &&\n typeof block.data === 'string' &&\n typeof block.mimeType === 'string' &&\n mediaClient\n ) {\n // Outbound agent image: upload immediately and send as a threaded m.image.\n const ctx = sessions.get(event.sessionId)\n if (ctx) {\n const bytes = Buffer.from(block.data, 'base64')\n const ext = (block.mimeType.split('/')[1] ?? 'png').replace(/[^a-z0-9]/gi, '')\n const filename = `image.${ext}`\n void mediaClient\n .upload({ data: bytes, contentType: block.mimeType, filename, asUserId: ctx.agent.userId })\n .then(({ content_uri }) =>\n client.sendMessage({\n roomId: ctx.roomId,\n asUserId: ctx.agent.userId,\n threadRoot: ctx.threadRoot,\n content: {\n msgtype: 'm.image',\n body: filename,\n url: content_uri,\n info: { mimetype: block.mimeType, size: bytes.length },\n },\n }),\n )\n .catch((err) => {\n console.warn(`[matrix:${name}] outbound image upload failed:`, err)\n void sendMediaError(ctx, err, 'agent image upload failed', client)\n })\n }\n } else {\n console.warn(`[matrix:${name}] dropped chunk block type=${block.type}`, block)\n }\n return\n }\n\n // An out-of-band event (tool_call / tool_call_update / plan) after some\n // buffered text means that assistant message is complete — flush it first\n // so it lands before this event on the wire, preserving interleaving.\n flushBuffer(event.sessionId)\n\n const eventType =\n event.type === 'tool_call'\n ? 'dev.zooid.tool_call'\n : event.type === 'tool_call_update'\n ? 'dev.zooid.tool_call_update'\n : event.type === 'available_commands'\n ? 'dev.zooid.available_commands_update'\n : 'dev.zooid.plan'\n const body =\n event.type === 'tool_call'\n ? toToolCallBody(event)\n : event.type === 'tool_call_update'\n ? toUpdateBody(event)\n : event.type === 'available_commands'\n ? toAvailableCommandsBody(event)\n : toPlanBody(event)\n body['m.relates_to'] = { rel_type: 'm.thread', event_id: ctx.threadRoot }\n const tail = (sendQueue.get(event.sessionId) ?? Promise.resolve()).then(async () => {\n try {\n await client.sendCustomEvent({\n roomId: ctx.roomId,\n asUserId: ctx.agent.userId,\n eventType,\n content: body,\n })\n } catch (err) {\n console.warn(`[matrix:${name}] sendCustomEvent(${eventType}) failed:`, err)\n }\n })\n sendQueue.set(event.sessionId, tail)\n await tail\n }\n\n agents.onApprovalRequest = async (name, req) => {\n const handle = approvals.register(name, (req as { sessionId: string }).sessionId, req, {\n timeoutMs: agents.getApprovalTimeoutMs(name),\n })\n return handle.decisionPromise\n }\n\n approvals.on('registered', (handle: RegisteredApproval) => {\n const ctx = sessions.get(handle.sessionId)\n if (!ctx) return\n const content: Record<string, unknown> = {\n approval_id: handle.approvalId,\n session_id: handle.sessionId,\n tool_call_id: handle.toolCallId,\n options: handle.options,\n }\n content['m.relates_to'] = { rel_type: 'm.thread', event_id: ctx.threadRoot }\n if (handle.toolKind !== undefined) content.tool_kind = handle.toolKind\n if (handle.toolTitle !== undefined) content.tool_title = handle.toolTitle\n if (handle.toolInput !== undefined) content.tool_input = handle.toolInput\n void client.sendCustomEvent({\n roomId: ctx.roomId,\n asUserId: ctx.agent.userId,\n eventType: 'dev.zooid.approval_request',\n content,\n })\n })\n\n async function handleInboundEvent(evt: MatrixEvent): Promise<void> {\n if (evt.event_id) {\n if (seenEventIds.has(evt.event_id)) {\n return\n }\n seenEventIds.add(evt.event_id)\n if (seenEventIds.size > SEEN_EVENT_CAP) {\n const first = seenEventIds.values().next().value\n if (first !== undefined) seenEventIds.delete(first)\n }\n }\n if (\n evt.origin_server_ts !== undefined &&\n evt.origin_server_ts < cutoffTs &&\n evt.type === 'm.room.message'\n ) {\n console.log(\n `[matrix] dropping stale message event ${evt.event_id} ` +\n `(ts=${evt.origin_server_ts}, daemon started at ${cutoffTs + STARTUP_GRACE_MS})`,\n )\n return\n }\n if (evt.type === 'm.room.member' && evt.content?.membership === 'invite') {\n const target = evt.state_key\n const inviter = evt.sender\n if (\n target &&\n evt.room_id &&\n ourBotUserIds.has(target) &&\n (!inviter || !ourBotUserIds.has(inviter))\n ) {\n console.log(\n `[matrix] declining ad-hoc invite for ${target} in ${evt.room_id} ` +\n `from ${inviter ?? 'unknown'}`,\n )\n await client\n .leaveRoom(evt.room_id, target, { reason: DECLINE_REASON })\n .catch((err) =>\n console.warn(`[matrix] leaveRoom(${evt.room_id}, ${target}) failed:`, err),\n )\n }\n return\n }\n if (evt.type === 'dev.zooid.session_reset') {\n // Spec § /clear: room-scope reset is unsupported. Only thread-scoped\n // resets carry a thread relation; drop bare room-level resets silently.\n const relates = evt.content?.['m.relates_to'] as\n | { rel_type?: string; event_id?: string }\n | undefined\n const threadRoot =\n relates?.rel_type === 'm.thread' && relates.event_id ? relates.event_id : undefined\n if (!threadRoot) {\n console.log('[matrix] dropping dev.zooid.session_reset without thread relation')\n return\n }\n console.log(`[matrix] inbound dev.zooid.session_reset in ${evt.room_id} thread=${threadRoot}`)\n // [[ZOD071]]: a thread's sessions are the thread-level one plus one per\n // handoff arc — end them all. Reset events aren't m.room.message, so\n // the self-heal rebuild above doesn't cover them; rebuild here if the\n // daemon restarted since the arcs were minted.\n if (!threadStates.has(threadRoot) && evt.room_id) {\n try {\n threadStates.set(\n threadRoot,\n await rebuildThreadState(client, evt.room_id, threadRoot, bindings),\n )\n } catch (err) {\n console.warn(`[matrix] failed to rebuild threadState for reset ${threadRoot}:`, err)\n }\n }\n const st = threadStates.get(threadRoot)\n for (const a of bindings) {\n agents.endSession(a.name, threadRoot)\n for (const arc of st?.handoffs[a.name] ?? []) {\n agents.endSession(a.name, composeHandoffKey(threadRoot, arc))\n }\n }\n // NB: keep threadStates intact. Per ZOD039 § /clear, only the agent's\n // session memory is wiped — thread-routing state (participants /\n // root-mentions) must survive so the next bare reply still routes to\n // the most-recently-posting agent under the same sessionKey.\n return\n }\n if (evt.type === 'dev.zooid.interrupt') {\n const content = (evt.content ?? {}) as { session_id?: string; reason?: string }\n // Thread-relation form (client-friendly): /interrupt in a thread sends\n // an empty event with `m.relates_to: thread/<root>`. Cancel every\n // session whose threadRoot matches.\n const relates = evt.content?.['m.relates_to'] as\n | { rel_type?: string; event_id?: string }\n | undefined\n const threadRoot =\n relates?.rel_type === 'm.thread' && relates.event_id ? relates.event_id : undefined\n if (threadRoot) {\n const targets: Array<{ sessionId: string; agent: string }> = []\n for (const [sessionId, ctx] of sessions) {\n if (ctx.threadRoot === threadRoot) {\n targets.push({ sessionId, agent: ctx.agent.name })\n }\n }\n for (const t of targets) {\n console.log(\n `[matrix] interrupt session=${t.sessionId} agent=${t.agent} thread=${threadRoot}` +\n (content.reason ? ` reason=${content.reason}` : ''),\n )\n await agents.cancelSession(t.agent, t.sessionId).catch((err) => {\n console.error(`[matrix] cancelSession(${t.agent}, ${t.sessionId}) failed:`, err)\n })\n }\n return\n }\n // Legacy form: explicit session_id in content.\n if (!content.session_id) {\n console.warn(`[matrix] dev.zooid.interrupt missing session_id (event_id=${evt.event_id})`)\n return\n }\n const ctx = sessions.get(content.session_id)\n if (!ctx) {\n return\n }\n console.log(\n `[matrix] interrupt session=${content.session_id} agent=${ctx.agent.name}` +\n (content.reason ? ` reason=${content.reason}` : ''),\n )\n await agents.cancelSession(ctx.agent.name, content.session_id).catch((err) => {\n console.error(`[matrix] cancelSession(${ctx.agent.name}, ${content.session_id}) failed:`, err)\n })\n return\n }\n if (evt.type === 'dev.zooid.approval_response') {\n const content = (evt.content ?? {}) as {\n approval_id?: string\n session_id?: string\n decision?: string\n option_id?: string\n }\n if (!content.session_id || !content.approval_id || !content.decision) return\n const decision = content.option_id\n ? { decision: content.decision, optionId: content.option_id }\n : { decision: content.decision }\n const ok = approvals.resolve(\n content.session_id,\n content.approval_id,\n decision as never,\n )\n if (!ok) console.warn(`[matrix] unknown approval ${content.approval_id}`)\n return\n }\n logInbound(evt)\n\n // Capture media events in the pending store; never route them to agents.\n if (\n evt.type === 'm.room.message' &&\n isMediaMsgtype(evt.content?.msgtype) &&\n evt.room_id &&\n evt.event_id &&\n evt.sender &&\n evt.content?.url &&\n !bindings.some((b) => b.userId === evt.sender)\n ) {\n pendingMedia.add(evt.room_id, inboundThreadRoot(evt), {\n eventId: evt.event_id,\n sender: evt.sender,\n msgtype: evt.content.msgtype as string,\n body: (evt.content.body as string | undefined) ?? '',\n filename: evt.content.filename as string | undefined,\n url: evt.content.url as string,\n info: evt.content.info as PendingMediaItem['info'],\n })\n return\n }\n\n // Agent-promotion: top-level inbound event becomes the thread root.\n // For in-thread messages the existing root is preserved.\n const promotedRoot = inboundThreadRoot(evt) ?? evt.event_id\n // Self-heal: if this is a thread reply but we have no in-memory state\n // for the root (e.g. daemon was just restarted), reconstruct it by\n // fetching the thread root + relations from the server.\n const inboundRel = inboundThreadRoot(evt)\n if (\n evt.type === 'm.room.message' &&\n inboundRel &&\n !threadStates.has(inboundRel) &&\n evt.room_id\n ) {\n try {\n const rebuilt = await rebuildThreadState(client, evt.room_id, inboundRel, bindings)\n threadStates.set(inboundRel, rebuilt)\n console.log(\n `[matrix] rebuilt threadState for ${inboundRel}: participants=${rebuilt.participants.join(',')} rootMentions=${rebuilt.rootMentions.join(',')}`,\n )\n } catch (err) {\n console.warn(`[matrix] failed to rebuild threadState for ${inboundRel}:`, err)\n }\n }\n const matches = route(evt, bindings, threadStates)\n // Suppress the no-match warning for events sent by our own bots.\n const senderIsBot = bindings.some((b) => b.userId === evt.sender)\n if (evt.type === 'm.room.message' && matches.length === 0 && !senderIsBot) {\n console.warn(\n `[matrix] no agent matched message in ${evt.room_id} from ${evt.sender}` +\n ` (bindings: ${bindings.map((b) => `${b.name}@${b.userId}[${b.trigger}]`).join(', ')})`,\n )\n }\n // Seed thread state for any agent mentions in this event.\n if (matches.length > 0 && promotedRoot) {\n let st = threadStates.get(promotedRoot)\n if (!st) {\n st = { participants: [], rootMentions: [], callers: {}, handoffs: {} }\n threadStates.set(promotedRoot, st)\n }\n const msgMentions = new Set(extractMentions(evt as never))\n const senderAgent = bindings.find((b) => b.userId === evt.sender)\n for (const a of bindings) {\n if (!msgMentions.has(a.userId)) continue\n if (!st.rootMentions.includes(a.name)) st.rootMentions.push(a.name)\n // Call edge: the (agent) sender is the caller of every agent it @mentions.\n if (senderAgent && a.name !== senderAgent.name) {\n st.callers[a.name] = senderAgent.name\n // [[ZOD071]] handoff arc: every agent→agent call opens a fresh\n // session for the callee, keyed by this call event. Recorded before\n // runTurn dispatch so sessionKeyFor resolves the just-minted arc.\n // Deduped: homeservers may redeliver a transaction.\n if (evt.event_id) {\n const arcs = (st.handoffs[a.name] ??= [])\n if (!arcs.includes(evt.event_id)) arcs.push(evt.event_id)\n }\n }\n }\n }\n for (const a of matches) {\n console.log(`[matrix] → ${a.name} (${a.userId})`)\n void runTurn(a, evt)\n .then(() => {\n if (!promotedRoot) return\n let st = threadStates.get(promotedRoot)\n if (!st) {\n st = { participants: [], rootMentions: [], callers: {}, handoffs: {} }\n threadStates.set(promotedRoot, st)\n }\n if (st.participants.at(-1) !== a.name) st.participants.push(a.name)\n })\n .catch((err) => {\n console.error(`[matrix] runTurn failed for ${a.name}:`, err)\n const c = classify(err)\n const threadRoot = inboundThreadRoot(evt) ?? evt.event_id\n if (!threadRoot || !evt.room_id) return\n const body = toErrorBody(\n {\n kind: 'error',\n agentId: a.name,\n sessionId: null,\n turnId: null,\n code: c.code,\n message: err instanceof Error ? err.message : String(err),\n detail: err instanceof Error && err.stack ? err.stack.slice(0, 2000) : undefined,\n transient: c.transient,\n acp_error: c.acp_error,\n },\n threadRoot,\n )\n void client\n .sendCustomEvent({\n roomId: evt.room_id,\n asUserId: a.userId,\n eventType: 'dev.zooid.error',\n content: body,\n })\n .catch((e) => console.warn(`[matrix:${a.name}] dev.zooid.error send failed:`, e))\n })\n }\n }\n\n const app = new Hono()\n\n function authOk(authHeader: string | undefined): boolean {\n const h = authHeader ?? ''\n if (!h.startsWith('Bearer ')) return false\n const got = h.slice(7)\n if (got.length !== hsToken.length) return false\n return timingSafeEqual(Buffer.from(got), Buffer.from(hsToken))\n }\n\n app.put('/_matrix/app/v1/transactions/:txnId', async (c) => {\n if (!authOk(c.req.header('authorization'))) {\n return c.json({ errcode: 'M_FORBIDDEN' }, 403)\n }\n const body = (await c.req.json().catch(() => ({}))) as { events?: MatrixEvent[] }\n for (const evt of body.events ?? []) {\n await handleInboundEvent(evt)\n }\n return c.json({})\n })\n\n app.get('/_matrix/app/v1/users/:userId', (c) => {\n if (!authOk(c.req.header('authorization'))) {\n return c.json({ errcode: 'M_FORBIDDEN' }, 403)\n }\n return c.json({})\n })\n app.get('/_matrix/app/v1/rooms/:alias', (c) => {\n if (!authOk(c.req.header('authorization'))) {\n return c.json({ errcode: 'M_FORBIDDEN' }, 403)\n }\n return c.json({ errcode: 'M_NOT_FOUND' }, 404)\n })\n app.post('/_matrix/app/v1/ping', (c) => {\n if (!authOk(c.req.header('authorization'))) {\n return c.json({ errcode: 'M_FORBIDDEN' }, 403)\n }\n return c.json({})\n })\n app.get('/healthz', (c) => c.text('ok'))\n\n async function runTurn(agent: AgentBinding, evt: MatrixEvent): Promise<void> {\n if (!evt.room_id || !evt.event_id) return\n const inbound = inboundThreadRoot(evt)\n // Agent-promotion: top-level inbound becomes a thread root via the agent's\n // first reply.\n const threadRoot = inbound ?? evt.event_id\n // [[ZOD071]]: the session key is the agent's current handoff arc when it\n // has one, else the thread-level key. The raw threadRoot still travels\n // separately: outbound events relate to it, and it is the context ref so\n // zooid_get_history reads the real thread.\n const sessionKey = sessionKeyFor(agent.name, threadRoot, threadStates.get(threadRoot))\n const sessionId = await agents.ensureSession(agent.name, sessionKey, evt.room_id, threadRoot)\n sessions.set(sessionId, { agent, roomId: evt.room_id, threadRoot })\n buffers.set(sessionId, '')\n bufferMessageIds.delete(sessionId)\n flushedCounts.set(sessionId, 0)\n // Commands the shim advertised during ensureSession (session load/new)\n // arrived before the ctx above existed and were stashed — replay the latest\n // now that the session is fully registered, so the palette actually fills.\n const stashedCommands = pendingCommands.get(sessionId)\n if (stashedCommands) {\n pendingCommands.delete(sessionId)\n void agents.onEvent?.(agent.name, stashedCommands)\n }\n\n const roomId = evt.room_id\n const TYPING_TTL_MS = 30_000\n const TYPING_REFRESH_MS = 25_000\n const safeTyping = (typing: boolean) =>\n client\n .setTyping({ roomId, asUserId: agent.userId, typing, timeoutMs: TYPING_TTL_MS })\n .catch((err) => console.warn(`[matrix:${agent.name}] setTyping(${typing}) failed:`, err))\n const safePresence = (presence: 'online' | 'unavailable' | 'offline') =>\n client\n .setPresence({ asUserId: agent.userId, presence })\n .catch((err) =>\n console.warn(`[matrix:${agent.name}] setPresence(${presence}) failed:`, err),\n )\n\n await safeTyping(true)\n await safePresence('unavailable')\n const refresh = setInterval(() => {\n void safeTyping(true)\n }, TYPING_REFRESH_MS)\n\n try {\n const rawBody = evt.content?.body ?? ''\n const promptText = stripMention(rawBody, agent.userId)\n\n // Drain pending media for this sender+thread and prepend as ACP content blocks.\n const pendingItems = pendingMedia.drain(\n evt.room_id,\n inboundThreadRoot(evt),\n evt.sender ?? '',\n )\n const { blocks, pathLines } = await buildMediaBlocks(pendingItems, {\n agent,\n media: mediaClient,\n writeAttachmentFn,\n onError: (item, err) => {\n console.warn(`[matrix:${agent.name}] media_failed for ${item.body}:`, err)\n void sendMediaError(\n { agent, roomId: evt.room_id!, threadRoot },\n err,\n `Could not process attachment: ${item.body}`,\n client,\n )\n },\n })\n\n const fullPromptText = [promptText, ...pathLines].filter(Boolean).join('\\n')\n await agents.prompt(agent.name, {\n threadId: sessionKey,\n channelId: evt.room_id,\n contextThreadId: threadRoot,\n content: [...blocks, { type: 'text', text: fullPromptText }],\n })\n // Drain: the prompt promise resolves on the stopReason response, but\n // trailing chunks may still arrive (see DRAIN_* above). Wait until the\n // buffer is quiet for DRAIN_QUIET_MS, re-arming on each new chunk.\n //\n // Subtlety: some agents (opencode in particular) resolve `session/prompt`\n // *before* the agent_message_chunk stream starts. So the buffer can be\n // empty for several seconds after prompt resolves, and only then do the\n // chunks arrive. We can't break the drain just because the buffer is\n // empty — we have to wait up to drainMaxMs for chunks to *start*. Once\n // any content arrives, the \"quiet for drainQuietMs\" rule kicks in.\n const drainStart = Date.now()\n let drained = buffers.get(sessionId) ?? ''\n while (drainQuietMs > 0 && Date.now() - drainStart < drainMaxMs) {\n await delay(drainQuietMs)\n const next = buffers.get(sessionId) ?? ''\n // Stop when the buffer is quiet (unchanged) and either it holds the\n // final message to flush, or we already flushed a message this turn\n // (so an empty, quiet buffer means the turn is genuinely done — the\n // last message was flushed mid-stream). An unchanged *empty* buffer\n // with nothing flushed yet means the stream hasn't started; keep\n // waiting up to drainMaxMs.\n if (next === drained && (next.length > 0 || (flushedCounts.get(sessionId) ?? 0) > 0))\n break\n drained = next\n }\n // Flush the final assistant message — the one with no following messageId\n // change or out-of-band event to have triggered an earlier flush.\n flushBuffer(sessionId)\n } finally {\n clearInterval(refresh)\n await safeTyping(false)\n await safePresence('online')\n // Wait for every queued send (mid-turn flushes, tool/plan events, final\n // flush) to settle before announcing the turn's end — and run this even\n // when the turn above threw, so the room never hangs on a spinner.\n await (sendQueue.get(sessionId) ?? Promise.resolve())\n const producedOutput = (flushedCounts.get(sessionId) ?? 0) > 0\n if (!producedOutput) {\n console.warn(\n `[matrix:${agent.name}] turn finished with empty buffer (session=${sessionId}); nothing sent to ${evt.room_id}`,\n )\n }\n // Turn boundary for [[ZOD076]] and push notifications. Sent after the\n // send queue drains so it lands *after* the prose it announces — a\n // turn.end arriving first would notify the user to look at a room that\n // has nothing in it yet.\n await client\n .sendCustomEvent({\n roomId: evt.room_id,\n asUserId: agent.userId,\n eventType: 'dev.zooid.turn.end',\n content: toTurnEndBody(\n { agentId: agent.name, sessionId, producedOutput, lastMessage: lastFlushed.get(sessionId) },\n threadRoot,\n ),\n })\n .catch((e) => console.warn(`[matrix:${agent.name}] turn.end send failed:`, e))\n buffers.delete(sessionId)\n bufferMessageIds.delete(sessionId)\n flushedCounts.delete(sessionId)\n lastFlushed.delete(sessionId)\n sendQueue.delete(sessionId)\n }\n }\n\n const syncLoops: SyncLoop[] | undefined =\n mode === 'client'\n ? bindings.map(\n (b) =>\n new SyncLoop({\n client: client as never,\n asUserId: b.userId,\n loadSince: () => opts.loadSince?.(b.userId) ?? null,\n saveSince: (since) => opts.saveSince?.(b.userId, since),\n onEvent: (evt) => handleInboundEvent(evt as MatrixEvent),\n }),\n )\n : undefined\n\n return {\n app,\n syncLoops,\n bootstrap: async (\n bootstrapOpts: {\n spaceRoomId?: string\n asUserId?: string\n adminUserIds?: string[]\n } = {},\n ) => {\n await pool.bootstrap({ adminUserId, ...bootstrapOpts })\n await Promise.allSettled(\n bindings.map((b) =>\n client.setPresence({ asUserId: b.userId, presence: 'online' }).catch((err) => {\n console.warn(`[matrix:${b.name}] initial setPresence(online) failed:`, err)\n }),\n ),\n )\n },\n pool,\n }\n}\n\n/**\n * Reconstruct the in-memory ThreadState for a thread root by fetching the\n * root event + its thread relations from the server. Used to recover the\n * implicit-routing rule from ZOD039 § Implicit triggers in threads after a\n * daemon restart wipes the in-memory cache.\n */\nexport async function rebuildThreadState(\n client: MatrixClient,\n roomId: string,\n rootEventId: string,\n bindings: AgentBinding[],\n): Promise<ThreadState> {\n const state: ThreadState = { participants: [], rootMentions: [], callers: {}, handoffs: {} }\n // Impersonate an agent that's actually a member of this room (AS reads\n // require room membership). Falling through to the first binding would\n // 403 if that agent never joined the target room.\n const asUser = (bindings.find((b) => b.rooms.some((r) => r.alias === roomId)) ?? bindings[0])?.userId\n if (!asUser) return state\n\n const root = await client.fetchEvent(roomId, rootEventId, asUser)\n if (root) {\n const rootMentions = new Set(extractMentions(root as never))\n const rootSender = (root as { sender?: string }).sender\n const rootSenderAgent = rootSender ? bindings.find((b) => b.userId === rootSender) : undefined\n for (const a of bindings) {\n if (!rootMentions.has(a.userId)) continue\n if (!state.rootMentions.includes(a.name)) state.rootMentions.push(a.name)\n if (rootSenderAgent && a.name !== rootSenderAgent.name) {\n state.callers[a.name] = rootSenderAgent.name\n const arcs = (state.handoffs[a.name] ??= [])\n if (!arcs.includes(rootEventId)) arcs.push(rootEventId)\n }\n }\n }\n\n const { chunk: thread } = await client.fetchThreadRelations({\n roomId,\n rootEventId,\n asUserId: asUser,\n })\n // Also seed root-mentions from any subsequent agent @mentions in the thread.\n for (const ev of thread) {\n const mentions = new Set(extractMentions(ev as never))\n const evSender = (ev as { sender?: string }).sender\n const evSenderAgent = evSender ? bindings.find((b) => b.userId === evSender) : undefined\n const evId = (ev as { event_id?: string }).event_id\n for (const a of bindings) {\n if (!mentions.has(a.userId)) continue\n if (!state.rootMentions.includes(a.name)) state.rootMentions.push(a.name)\n if (evSenderAgent && a.name !== evSenderAgent.name) {\n state.callers[a.name] = evSenderAgent.name\n if (evId) {\n const arcs = (state.handoffs[a.name] ??= [])\n if (!arcs.includes(evId)) arcs.push(evId)\n }\n }\n }\n const type = (ev as { type?: string }).type\n if (type === 'm.room.message' && evSender) {\n const a = bindings.find((b) => b.userId === evSender)\n if (a && state.participants.at(-1) !== a.name) state.participants.push(a.name)\n }\n }\n return state\n}\n\nfunction logInbound(evt: MatrixEvent): void {\n const sender = evt.sender ?? '?'\n const room = evt.room_id ?? '?'\n const type = evt.type ?? '?'\n if (type === 'm.room.message') {\n const body = evt.content?.body ?? ''\n const mentions = (evt.content?.['m.mentions'] as { user_ids?: string[] } | undefined)?.user_ids\n const mentionsStr = mentions?.length ? ` mentions=${JSON.stringify(mentions)}` : ''\n console.log(\n `[matrix] inbound msg in ${room} from ${sender}${mentionsStr}: ${truncate(body, 200)}`,\n )\n } else {\n console.log(`[matrix] inbound ${type} in ${room} from ${sender}`)\n }\n}\n\nfunction truncate(s: string, n: number): string {\n return s.length > n ? s.slice(0, n) + '…' : s\n}\n","import type { ThreadState } from './router.js'\n\n/**\n * Separator between a thread root and a handoff-arc call event id inside a\n * session key. Matrix event IDs ('$' + URL-safe base64; legacy '$hash:domain')\n * never contain '|', so composed keys are unambiguous — though nothing below\n * the transport ever parses one: the key is opaque downstream ([[ZOD071]]).\n */\nexport const HANDOFF_KEY_SEP = '|'\n\nexport function composeHandoffKey(threadRoot: string, callEventId: string): string {\n return `${threadRoot}${HANDOFF_KEY_SEP}${callEventId}`\n}\n\n/**\n * The session key for an agent's next turn in a thread: its latest handoff\n * arc when it has been called (agent→agent @mention, [[ZOD071]]), else the\n * thread-level key. Relies on the transaction handler recording the arc\n * BEFORE the turn is dispatched, so a just-called sub resolves to the arc\n * minted by its own triggering event.\n */\nexport function sessionKeyFor(\n agentName: string,\n threadRoot: string,\n state: ThreadState | undefined,\n): string {\n const arc = state?.handoffs[agentName]?.at(-1)\n return arc ? composeHandoffKey(threadRoot, arc) : threadRoot\n}\n","import type {\n AvailableCommandsEvent,\n PlanEvent,\n TapEvent,\n ToolCallEvent,\n ToolCallUpdateEvent,\n} from '@zooid/acp-client'\n\n/** Cap any single string in rawInput so big diffs / file contents don't bloat Matrix. */\nconst RAW_INPUT_STR_MAX = 250\n\nexport function toToolCallBody(evt: ToolCallEvent): Record<string, unknown> {\n const out: Record<string, unknown> = {\n session_id: evt.sessionId,\n tool_call_id: evt.toolCallId,\n title: evt.title,\n }\n if (evt.kind !== undefined) out.kind = evt.kind\n if (evt.status !== undefined) out.status = evt.status\n if (evt.rawInput !== undefined) out.raw_input = truncateStrings(evt.rawInput, RAW_INPUT_STR_MAX)\n if (evt.locations !== undefined) out.locations = evt.locations\n return out\n}\n\n/**\n * Recursively truncates string values longer than `max` with a \"… [truncated]\"\n * suffix. Non-string scalars and structure are preserved.\n */\nfunction truncateStrings(v: unknown, max: number): unknown {\n if (typeof v === 'string') {\n return v.length > max ? v.slice(0, max) + '… [truncated]' : v\n }\n if (Array.isArray(v)) {\n return v.map((item) => truncateStrings(item, max))\n }\n if (v && typeof v === 'object') {\n const out: Record<string, unknown> = {}\n for (const [k, val] of Object.entries(v as Record<string, unknown>)) {\n out[k] = truncateStrings(val, max)\n }\n return out\n }\n return v\n}\n\nexport function toUpdateBody(evt: ToolCallUpdateEvent): Record<string, unknown> {\n const out: Record<string, unknown> = {\n session_id: evt.sessionId,\n tool_call_id: evt.toolCallId,\n }\n if (evt.status !== undefined) out.status = evt.status\n if (evt.kind !== undefined) out.kind = evt.kind\n // content[] carries display-ready output (text/diff/terminal); rawOutput is\n // intentionally NOT serialized — it's typically large and duplicates content.\n if (evt.content !== undefined) out.content = evt.content\n // Some ACP agents only set rawInput on a later update (not the initial\n // tool_call). Truncate strings and forward.\n if (evt.rawInput !== undefined) out.raw_input = truncateStrings(evt.rawInput, RAW_INPUT_STR_MAX)\n if (evt.locations !== undefined) out.locations = evt.locations\n return out\n}\n\nexport function toPlanBody(evt: PlanEvent): Record<string, unknown> {\n return {\n session_id: evt.sessionId,\n entries: evt.entries,\n }\n}\n\nexport function toAvailableCommandsBody(\n evt: AvailableCommandsEvent,\n): Record<string, unknown> {\n return {\n session_id: evt.sessionId,\n available_commands: evt.commands.map((c) => ({\n name: c.name,\n description: c.description,\n })),\n }\n}\n\nconst RECOVERY_URLS: Partial<Record<string, string>> = {\n auth_missing: 'https://zooid.dev/docs/guides/run-in-container#authentication-that-carries-over',\n auth_invalid: 'https://zooid.dev/docs/guides/run-in-container#authentication-that-carries-over',\n mount_failed: 'https://zooid.dev/docs/guides/run-in-container#what-you-get-for-free',\n image_pull_failed: 'https://zooid.dev/docs/guides/run-in-container#skipping-the-image-prepull',\n}\n\ntype ErrorTap = Extract<TapEvent, { kind: 'error' }>\n\nexport function toErrorBody(evt: ErrorTap, threadRoot: string): Record<string, unknown> {\n const msg = evt.message.slice(0, 250)\n const out: Record<string, unknown> = {\n // No msgtype: dev.zooid.error is not m.room.message, so the field is\n // meaningless here — it was a vestige of copying the message-body shape.\n // Its presence used to force careful push-rule `before` positioning\n // (ZNC025 §10); that positioning is kept regardless, since it also\n // protects rules for event types that never carried the field.\n body: `⚠ [${evt.code}] ${msg}`,\n code: evt.code,\n message: msg,\n transient: evt.transient,\n 'm.relates_to': { rel_type: 'm.thread', event_id: threadRoot },\n }\n if (evt.sessionId) out.session_id = evt.sessionId\n if (evt.turnId) out.turn_id = evt.turnId\n if (evt.detail) out.detail = evt.detail.slice(0, 2000)\n if (evt.acp_error) out.acp_error = evt.acp_error\n const recovery = RECOVERY_URLS[evt.code]\n if (recovery) out.recovery = recovery\n return out\n}\n\nexport interface TurnEnd {\n agentId: string\n sessionId: string\n producedOutput: boolean\n /** The turn's final assistant message, for the push notification's preview. */\n lastMessage?: string\n}\n\n/** Push payloads are size-capped, and a notification body is glanceable or useless. */\nconst PREVIEW_MAX = 140\n\n/**\n * Turn-boundary marker for [[ZOD076]] and push notifications. Carries no\n * `msgtype` — a vestigial one (as `toErrorBody` used to carry) would collide\n * with `.m.rule.suppress_notices`'s type-agnostic match and silently swallow\n * the event before the [[ZNC025]] agent push rule ever sees it.\n */\nexport function toTurnEndBody(evt: TurnEnd, threadRoot: string): Record<string, unknown> {\n const preview = evt.lastMessage?.trim().replace(/\\s+/g, ' ')\n return {\n // `body` stays the turn-boundary summary: it is what a generic Matrix\n // client renders for this event, and the prose is already its own message\n // in the timeline. The preview below exists only for the push, which\n // cannot see that message — agent prose is `m.notice`, deliberately\n // silenced by `.m.rule.suppress_notices` so a chatty turn doesn't fire one\n // push per chunk ([[ZNC025]] §10). Without it the only notification the\n // user gets says an agent finished and nothing about what it said.\n body: evt.producedOutput ? `${evt.agentId} finished` : `${evt.agentId} finished without output`,\n ...(preview ? { last_message: preview.slice(0, PREVIEW_MAX) } : {}),\n agent_id: evt.agentId,\n session_id: evt.sessionId,\n produced_output: evt.producedOutput,\n 'm.relates_to': { rel_type: 'm.thread', event_id: threadRoot },\n }\n}\n","import { marked } from 'marked'\nimport sanitizeHtml from 'sanitize-html'\n\n// Matrix-permitted HTML tags per the m.room.message spec.\n// https://spec.matrix.org/v1.11/client-server-api/#mroommessage-msgtypes\nconst ALLOWED_TAGS = [\n 'del', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'blockquote', 'p', 'a',\n 'ul', 'ol', 'sup', 'sub', 'li', 'b', 'i', 'u', 'strong', 'em', 's',\n 'code', 'hr', 'br', 'div', 'table', 'thead', 'tbody', 'tr', 'th',\n 'td', 'caption', 'pre', 'span', 'img', 'details', 'summary',\n]\n\nconst ALLOWED_ATTRIBUTES: Record<string, string[]> = {\n a: ['href', 'name', 'target'],\n img: ['width', 'height', 'alt', 'title', 'src'],\n ol: ['start'],\n code: ['class'],\n span: ['data-mx-color', 'data-mx-bg-color', 'data-mx-spoiler'],\n}\n\nconst ALLOWED_SCHEMES = ['https', 'http', 'ftp', 'mailto', 'magnet', 'matrix']\n\nmarked.setOptions({ gfm: true, breaks: false, async: false })\n\nexport function toMatrixHtml(markdown: string): string {\n if (!markdown || !markdown.trim()) return ''\n let rawHtml: string\n try {\n const out = marked.parse(markdown) as string | Promise<string>\n if (typeof out !== 'string') return ''\n rawHtml = out\n } catch {\n return ''\n }\n return sanitizeHtml(rawHtml, {\n allowedTags: ALLOWED_TAGS,\n allowedAttributes: ALLOWED_ATTRIBUTES,\n allowedSchemes: ALLOWED_SCHEMES,\n allowedSchemesByTag: { a: ALLOWED_SCHEMES },\n })\n}\n","export const MAX_MEDIA_PER_TURN = 8\n\nexport interface PendingMediaItem {\n eventId: string\n sender: string\n msgtype: string\n body: string\n filename?: string\n url: string\n info?: { mimetype?: string; size?: number; w?: number; h?: number }\n}\n\nexport class PendingMediaStore {\n private readonly queues = new Map<string, PendingMediaItem[]>()\n\n private key(roomId: string, threadKey: string | undefined): string {\n return `${roomId} ${threadKey ?? 'main'}`\n }\n\n add(roomId: string, threadKey: string | undefined, item: PendingMediaItem): void {\n const k = this.key(roomId, threadKey)\n const q = this.queues.get(k) ?? []\n q.push(item)\n while (q.length > MAX_MEDIA_PER_TURN) q.shift()\n this.queues.set(k, q)\n }\n\n drain(roomId: string, threadKey: string | undefined, sender: string): PendingMediaItem[] {\n const k = this.key(roomId, threadKey)\n const q = this.queues.get(k) ?? []\n const mine = q.filter((i) => i.sender === sender)\n const rest = q.filter((i) => i.sender !== sender)\n if (rest.length) this.queues.set(k, rest)\n else this.queues.delete(k)\n return mine\n }\n}\n","/** Limits are routing policy, not enforcement — see ZOD057 (enforcement lives\n * in Tuwunel config + the Zoon composer). */\nexport const MAX_INLINE_IMAGE_BYTES = 524_288\nexport const INLINE_IMAGE_MIMES = ['image/png', 'image/jpeg', 'image/gif', 'image/webp']\nexport const MAX_DOWNLOAD_BYTES = 33_554_432\n\nexport interface MediaClientOptions {\n homeserver: string\n asToken: string\n fetch?: typeof globalThis.fetch\n}\n\nexport function parseMxcUri(uri: string): { serverName: string; mediaId: string } | null {\n const m = /^mxc:\\/\\/([^/]+)\\/(.+)$/.exec(uri)\n return m ? { serverName: m[1], mediaId: m[2] } : null\n}\n\nexport class MediaClient {\n private readonly homeserver: string\n private readonly asToken: string\n private readonly fetch: typeof globalThis.fetch\n\n constructor(opts: MediaClientOptions) {\n this.homeserver = opts.homeserver.replace(/\\/$/, '')\n this.asToken = opts.asToken\n this.fetch = opts.fetch ?? globalThis.fetch\n }\n\n async download(input: {\n mxcUri: string\n asUserId: string\n maxBytes?: number\n }): Promise<{ data: Uint8Array; contentType: string }> {\n const parsed = parseMxcUri(input.mxcUri)\n if (!parsed) throw new Error(`not an mxc uri: ${input.mxcUri}`)\n const url =\n `${this.homeserver}/_matrix/client/v1/media/download/` +\n `${encodeURIComponent(parsed.serverName)}/${encodeURIComponent(parsed.mediaId)}` +\n `?user_id=${encodeURIComponent(input.asUserId)}`\n const r = await this.fetch(url, {\n headers: { Authorization: `Bearer ${this.asToken}` },\n })\n if (!r.ok) throw new Error(`media download failed: ${r.status}`)\n const buf = new Uint8Array(await r.arrayBuffer())\n const max = input.maxBytes ?? MAX_DOWNLOAD_BYTES\n if (buf.byteLength > max) {\n throw new Error(`media too large: ${buf.byteLength} > ${max}`)\n }\n return { data: buf, contentType: r.headers.get('content-type') ?? 'application/octet-stream' }\n }\n\n async upload(input: {\n data: Uint8Array\n contentType: string\n filename?: string\n asUserId: string\n }): Promise<{ content_uri: string }> {\n const params = new URLSearchParams()\n if (input.filename) params.set('filename', input.filename)\n params.set('user_id', input.asUserId)\n const r = await this.fetch(`${this.homeserver}/_matrix/media/v3/upload?${params}`, {\n method: 'POST',\n headers: { Authorization: `Bearer ${this.asToken}`, 'Content-Type': input.contentType },\n body: input.data,\n })\n if (!r.ok) throw new Error(`media upload failed: ${r.status}`)\n return (await r.json()) as { content_uri: string }\n }\n}\n","import { mkdirSync, writeFileSync } from 'node:fs'\nimport { join } from 'node:path'\nimport { posix } from 'node:path'\n\nfunction sanitize(s: string, fallback: string): string {\n const cleaned = s.replace(/[^A-Za-z0-9._-]/g, '').replace(/^\\.+/, '')\n return cleaned || fallback\n}\n\nexport interface WriteAttachmentInput {\n workspaceDir: string\n agentWorkspacePath: string\n eventId: string\n filename: string\n data: Uint8Array\n}\n\nexport function writeAttachment(input: WriteAttachmentInput): {\n hostPath: string\n agentPath: string\n} {\n const dir = sanitize(input.eventId, 'event')\n const name = sanitize(input.filename, 'file')\n const hostDir = join(input.workspaceDir, '.zooid', 'attachments', dir)\n mkdirSync(hostDir, { recursive: true })\n const hostPath = join(hostDir, name)\n writeFileSync(hostPath, input.data)\n const agentPath = posix.join(input.agentWorkspacePath, '.zooid', 'attachments', dir, name)\n return { hostPath, agentPath }\n}\n","export interface SyncResponse {\n next_batch: string\n // Real homeservers omit `rooms` (and `rooms.join`) entirely on an idle\n // incremental sync — both are optional.\n rooms?: {\n join?: Record<string, {\n // A joined room may carry only state/ephemeral/account_data on a given\n // sync, with no `timeline` (or a timeline with no `events`).\n timeline?: {\n events?: Record<string, unknown>[]\n prev_batch?: string\n limited?: boolean\n }\n }>\n }\n}\n\nexport interface SyncClient {\n sync(opts: { asUserId: string; since: string | null; timeoutMs: number }): Promise<SyncResponse>\n}\n\nexport interface SyncLoopOptions {\n client: SyncClient\n asUserId: string\n loadSince: () => string | null\n saveSince: (since: string) => void\n onEvent: (evt: Record<string, unknown>) => void | Promise<void>\n timeoutMs?: number\n /** Backoff after a failed tick before retrying. Default 5000ms. */\n retryDelayMs?: number\n}\n\nexport class SyncLoop {\n private readonly opts: SyncLoopOptions\n private running = false\n\n constructor(opts: SyncLoopOptions) {\n this.opts = opts\n }\n\n async tick(): Promise<void> {\n const since = this.opts.loadSince()\n const res = await this.opts.client.sync({\n asUserId: this.opts.asUserId,\n since,\n timeoutMs: this.opts.timeoutMs ?? 30_000,\n })\n for (const [roomId, roomState] of Object.entries(res.rooms?.join ?? {})) {\n for (const baseEvt of roomState.timeline?.events ?? []) {\n await this.opts.onEvent({ ...(baseEvt as Record<string, unknown>), room_id: roomId })\n }\n }\n this.opts.saveSince(res.next_batch)\n }\n\n async run(): Promise<void> {\n this.running = true\n while (this.running) {\n try {\n await this.tick()\n } catch (err) {\n // A transient /sync failure (network blip, sleep/wake, 5xx) must not\n // kill the loop — log and back off, then resume from the same `since`.\n if (!this.running) break\n console.warn(`[sync-loop] ${this.opts.asUserId} tick failed, retrying:`, err)\n await new Promise((r) => setTimeout(r, this.opts.retryDelayMs ?? 5_000))\n }\n }\n }\n\n stop(): void {\n this.running = false\n }\n}\n","import { MatrixClient } from './matrix-client.js'\nimport type { AgentBinding } from './router.js'\n\nexport interface WorkforceRoster {\n version: 1\n agents: { user_id: string; name: string; rooms: string[] }[]\n}\n\nexport function buildWorkforceRoster(agents: AgentBinding[]): WorkforceRoster {\n return {\n version: 1,\n agents: agents.map((a) => ({\n user_id: a.userId,\n name: a.name,\n rooms: a.rooms.map((r) => r.alias),\n })),\n }\n}\n\nexport interface PublishOpts {\n client: MatrixClient\n spaceRoomId: string\n asUserId: string\n agents: AgentBinding[]\n}\n\nexport async function publishWorkforce(opts: PublishOpts): Promise<void> {\n await opts.client.sendStateEvent({\n roomId: opts.spaceRoomId,\n asUserId: opts.asUserId,\n eventType: 'dev.zooid.workforce',\n stateKey: '',\n content: buildWorkforceRoster(opts.agents) as unknown as Record<string, unknown>,\n })\n}\n\nexport interface PublisherHandle {\n reload(): Promise<void>\n stop(): Promise<void>\n}\n\nexport interface StartOpts {\n client: MatrixClient\n spaceRoomId: string\n asUserId: string\n getAgents: () => AgentBinding[]\n}\n\nexport async function startWorkforcePublisher(opts: StartOpts): Promise<PublisherHandle> {\n await publishWorkforce({ ...opts, agents: opts.getAgents() })\n return {\n async reload() {\n await publishWorkforce({ ...opts, agents: opts.getAgents() })\n },\n async stop() {},\n }\n}\n"],"mappings":"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","names":["localpart","body","localpart","localpart","inboundThreadRoot","ctx"]}