@ai-matrx/messaging
企业,每个 Matrx 客户端的 AI- 本地应用消息: direct, group, and org- spected 与代理商的谈话,作为头等舱的参与者——作为一揽子服务提供 iMessage的抛光和Slack的力量.
1个供应商,1个部件,不到1小时,包括AI在内的完整制作信息.
pnpm add @ai-matrx/messaging
建造在 @ai-matrx/data/realtime。这里有 手卷为零 .channel(
此软件包中的代码而且你身上一定没有.
此软件包为何存在
通讯是每个人都认为容易的特征,没有船两次正确。 难 部件不是气泡 它们是八种方式 信息可以丢失,复制,还原, 或归咎于错人。 其中每一个都有一个名字 在这个README和一个回归 测试旁边.
我们是故意的 无弹性。没有提供者抽象,没有运输适配器,没有 "自己开店" 包裹就是我们想要它做的,那就是产品.
学说
1. 信息可以四条路到达。 它使一次.
乐观的气泡,插入器自己的反应,广播,和"Postgres"更改行——
任何顺序。 身份为 id 或者说 clientMessageId和确认的行 崩溃到
乐观的一位就位.
诱人的快捷键——匹配 (sender, content) ——悄悄地将两个真正不同的合并在一起
"OK" 消息变成一个。 客户意识的一能键存在,所以身份从来不必须是
猜对了 每条已排队的消息都会被刻出 每个 发送尝试, 也就是
当上次尝试实际到达数据库时,是什么使得重试安全.
2. 年长者从未超过新人.
背负老一点的更新 edited_at 而不是已经持有的副本 丢弃。 。 。 .
如果不这样做,编辑就会在一秒钟后明显恢复,然后重新应用.
唯一的例外是故意的:一行总是取代乐观的一行,即使是在 乐观的时间戳以后读取。 时间戳是从发送设备的时钟上猜到的, 而扭曲的时钟 绝不能将气泡固定为永久的"终止".
3.再通指再通. 实时没有重播 .
此软件包打开的每个频道都声明 onBackfill 门,这些门从
数据库——从自己的高水分标记获取对话频道,因此它获取它错过的东西
而不是所有的东西。 不重读的再连接会留下永久错误的线程
看起来很健康 这是最昂贵的信息错误, 它的结构
这里不可能.
4. 你输入的信息永远不会丢失.
收发箱坚持草稿 在此之前 网络呼叫,而不是在它成功之后。 保证:
- 耐用度超过重载. 一个已关闭的分页中端返回, 信件仍然排队 .
默认存储是
localStorage; 当无法使用时, 软件包会回到内存 财务报告和审定财务报表 这么说的 而不是装作持久. - 以序为序. 消息2从未超过重试消息1.
- 被束缚和诚实. 尝试退后; 最后一次之后, 条目留在队列中,
标记
failed, (中文(简体) ), 有个原因 并重试一扇门。 从不默不作声,从不重试 永远. - 可见相. 一个排队的消息立即变成一个乐观的泡沫。 隐形队列 是用户一方丢失的消息.
5. 数据库中设立了直接对话.
通过 dm_get_or_create_direct_conversation,哪个咨询锁定未排序的用户对。 这个
被禁模式是读取时插入:两个标签、双击或两个分批系统
通知将它拼为两个"直接"对话,一对,永远.
6. 授权是数据库的。 奥尔格是明确的.
RLS加上经认证的 安全DEFINER RPCs。 这个软件包中没有一个许可分支——
一个RPC,说不变成一个 forbidden 错误,绝不是客户端的再决定 。 每个
对话与信件书写带有明确的 organization_id; 包拒绝
而不是让一行未瞄准的行进入数据库。 这个组织是写作
仅限: organizationId={null} 是一个有效的状态,每个读取的作品(收件箱,
深层次的谈话,回答——答复印有对话的组织,
开始新对话被拒绝 organization-required 所以主持人可以问
一个人选择一个.
7. 缺失的会话是一个生命周期时刻,而不是一个错误.
读到在签到和签到之间 一次 在主机解决后
届会,然后报告 session-unavailable 作为 警告拒绝许可从未经过复审 --
重试一个是请求风暴是如何形成的. (事件:访问前安装的阅读器)
符号存在, 应用程序被捕获 909个错误,0.6秒.) 简介和清单也读取
每个密钥共享一个飞行中的承诺,所以没有同时阅读者发出一个请求——一个失败是
从来没有缓存, 或一个摇摆离开表面被打破, 直到重新装入.
8. 相接键是按键集,由一列独有终止.
(created_at, id)永远 created_at 单行——当两个共享时重复或跳过行
毫秒,即大圆形的每页. 软件包还要求比页面多一行
这样 hasMore 这是事实 而不是"我们得到了一整页"的猜测.
9. 代理人从不佩戴同事相貌.
sender_id 是审计的本金,永远不变。 当特工通过人类的行为
二、会议闭幕 已执行 作者是代理人——它为它行事的人取名。 无名
特工被贴上"特工"的标签 回到人类名下
在真实人物的旁白下留言.
10. 信息名称必须打开.
Matrx 参考文献以两种方式到达——结构 metadata.references,或作为 ```matrx
内容中的围栏。 两者都成为活牌. 栅栏 永远 作为代码到达读者
块状的 JSON, 并且从未作为原始 JSON 到达收件箱预览或桌面通知 ;
一个崩溃函数既服务于两个。 没有可解决身份的引用被删除,而不是
绘制为无法打开的卡片 .
11. 这种建设不能使任何事情成为荣誉.
可操作信息通过登记簿而不是 switch一个未知 kind - 或已知类型在
一个未知的 version ——不制造芯片,不执行任何内容,这是让新发货船能够完成的任务
在读者都赶上之前 三. 强制扩大 登记册: 同时进行
处决 (kind, messageId, actorId) 共享一个呼叫,一个已解析的动作返回存储
不触摸服务器就收到. 有效载荷不是授权的 - 处理者必须
重新解决耐用,呼叫者授权的行 之前写.
12. 没有任何结构化的骑行 user_input。 。 。 .
user_input 人类是什么样的类型。 记名、名册和截止日期旅行
变量。服务器存储和帧 user_input 作为转弯的人的话,所以
通过它走私记录片 悄悄地腐蚀了它触及的每一个谈话 那个
user_input 此软件包会发出一个输入“ 起草回复” 的指令 .
用这个
一小时的融合
import { MessagingProvider, MessagingInbox } from "@ai-matrx/messaging/react";
import "@ai-matrx/messaging/tokens.css"; // defaults — override with your brand
import "@ai-matrx/messaging/styles.css"; // structural, required
export default function MessagesPage() {
return (
<MessagingProvider
client={supabase}
organizationId={org.id}
>
<MessagingInbox />
</MessagingProvider>
);
}
这是实时的,存在,打字,乐观的发送, 离线的发件箱,引用,可操作 信息, 密钥集页码, 和整个被打磨的UI。 没有别的东西可以窃听了.
🚨 你没有告诉你自己是谁,你不能(0.12.0,DD-241). 代理用户读取
从 client自己的会话——即根据每项请求给JWT薄荷的会话——所以通过
浏览器 Supabase 客户端中的应用程序标志 。 跨越此边界的用户代号是 COPY,
一份拷贝在auth cookie旋转时会沉入水中
作为 sender_id 财务报告和审定财务报表 created_by,写出归属于某个标签不能证明是的行.
提供者是惰性的,直到第一个会话读取土地,一个账户切换器重建它,a
签了字就又让它变得惰性了 无框架路径的相同规则 :
createMessagingRepository({ client, organizationId }) 没有 identity。 。 。 .
增加大赦国际
<MessagingProvider
client={supabase}
organizationId={org.id}
transport={matrxTransport} // from @ai-matrx/api
agents={{
catchUp: { agentId: "…", configOverrides: { … } },
summarize: { agentId: "…" },
actionItems: { agentId: "…" },
draftReply: { agentId: "…" },
}}
maxTranscriptMessages={200} // your knob, not our taste
sourceApp="matrx-frontend" // YOUR registered producer slugs
sourceFeature="messages"
>
代理定义存在于 数据库,从不在此包中——注入 身份 实值 只有一部分是主机补给 无身份能力 完全没有实现这是 永远不要一个按下时失败的死按钮.
一个身份是两半. 如果您的应用程序从切换层解析了某个能力的代理
同时决定任务设置、通过 configOverrides 编号——是逐字发送的
作为转弯 config_overrides。单独通过 id 将设置降低一半。 别管了
并且没有发送任何信息,所以代理商自己的设置站立.
运行用户监视流 . useMessagingAi 报告 partialText 在每一块之后
ConversationView 骨架只在第一个块之前的空隙中出现
命名它正在等待的工作。 如果你自己完成输出, 完成 partialText 时
isRunning 一个坐到跑道结束的占位符 是一个站着的旋转器
答话.
🚨 sourceApp 编号 : sourceFeature 并不是分析的甘蔗. AI Matrx 服务器保存一个寄存器
合法生产者的答复 422个 - 所以每个AI电话都失败了 直到你
传出你的服务器知道的子弹 这个软件包的默认值是占位符;只有你知道你的.
"把我抓起来"就是未读的尾巴,来源于商店本身的未读数. 没什么 未读取,或者装入的窗口不能返回已读标记,意味着没有诚实 截取和整个窗口都适用——从来不是猜测的时间戳.
开放参考文献
<MessagingProvider … onOpenReference={(ref) => router.push(`/${ref.entityType}/${ref.entityId}`)}>
关闭它和参考卡 做成标签,非交互卡 标题解释为什么.. 老实说,从来没有一个按钮 什么也没有.
挂入已实时接线的应用程序
无所事事. <MessagingProvider> 骑着主机 <RealtimeProvider> 当一个被挂起时
在天上行走,在天上行走。 这不是一个方便,一个应用有一个
故意实时管理器(它拥有使乐观写作自己回声的写作分类账)
),而下面的第二个使每个经理的写作看起来很偏僻.
<RealtimeProvider client={supabase} actorId={user.id}> {/* the app's ONE mount */}
<MessagingProvider client={supabase} organizationId={org.id}>
…
正在注册可操作信件类型
<MessagingProvider … actions={[{
kind: "access_request",
versions: [1],
choices: () => [
{ id: "approve", label: "Approve", tone: "primary" },
{ id: "decline", label: "Decline", tone: "danger" },
],
execute: async (payload, ctx) => {
// Re-resolve the authorized request row here. The payload is the REQUEST,
// never the permission.
const outcome = await decideAccessRequest(payload.requestId, ctx.choice);
return { kind: "access_request", messageId: ctx.messageId, actorId: ctx.actorId,
outcome: outcome.alreadyAnswered ? "already" : "applied",
label: outcome.alreadyAnswered ? "Already answered" : "Approved",
settledAt: new Date().toISOString() };
},
}]}>
这是接缝 @ai-matrx/meet 使用: 一个呼叫邀请仅仅是一个结构化的信息
载荷.
一个动作型的答案是卡片 不是芯片
芯片回答一个问题。 共享资源卡, 链接到报告, 提醒和应用程序 图标是应用程序形状的表面,只有主机可以绘制——所以主机提供表面和 软件包仍然拥有大门 :
<MessagingProvider … actionRenderers={[{
kind: "resource_shared",
versions: [1],
render: ({ payload, isOwn }) => <ResourceSharedCard payload={payload} isOwn={isOwn} />,
}]}>
一种用制造器画的 而不是芯片 其他一切不变,包括:
规则很重要:一种未知的种类——或这种构件不列出的版本中已知的种类——
做没什么。 使用 actions 用于决定, actionRenderers 用于表面;一种可能具有
两人都是处理者 summarize,用于预览)和一个渲染器.
你的应用在我们的气泡上发热
表面全部按右键回答的平台, 或已经通过引用的平台 其自己的登记册,将那些不重建线条的人交给:
<MessagingProvider …
wrapMessage={({ message, children }) => (
<MessageContextMenu message={message}>{children}</MessageContextMenu>
)}
wrapConversationRow={({ conversation, children }) => (
<ConversationContextMenu conversation={conversation}>{children}</ConversationContextMenu>
)}
renderReference={({ reference }) => <AppReferenceChip reference={reference} />}
/>
wrapMessage 财务报告和审定财务报表 wrapConversationRow WRAP;它们从未被替换. renderReference 替换
软件包的卡片——故意的,因为一个在其它地方绘制引用的应用程序不能
画出两条路.
如果你的站台有自己的"马特克"栅栏方言,通过 renderFence 我也一样. 这个包
剖析一个形状( 包含 {entityType, entityId}{\fn华文楷体\fs20\bord1\shad1\1che0e0e0\2ch000000}能到出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出
逐字记录为栅栏体. 没有它,这些栅栏画 诚实的惰性"参考"卡片——他们
从来没有被删除, 从来没有显示给读者 作为JSON.
他们
包件中的结构式CSS舰; 符号 合同 执行; 符号 数值 为:
属于你的。 导入 tokens.css 完全工作默认,然后覆盖您所关心的名称
关于:
:root {
--mx-msg-accent: #7c3aed;
--mx-msg-bubble-mine-bg: var(--mx-msg-accent);
}
软件包组件中的硬码颜色被禁用,如果一个
六进制值出现在 styles.css。为两者定义了黑暗值 prefers-color-scheme 还有一个
明确 [data-theme="dark"],所以一个主机切换在两个方向都获胜.
构建自己的布局
<MessagingInbox /> 是产品。 如果您想要列表以边栏和路径上的线程表示:
<ConversationList onSelect={(id) => router.push(`/messages/${id}`)} />
<ConversationView conversationId={id} />
钩子.. useConversations, (中文(简体) ), useConversation, (中文(简体) ), useComposer, (中文(简体) ), useTypists, (中文(简体) ), useOnlineUserIds, (中文(简体) ),
useMessagingAi, (中文(简体) ), useMessageAction – 用于构成自己页面的主机导出. 他们
为: 没有 请求从部件中重建产品.
外部反应
默认条目是无框架的 —— 不受 Redux 中间软件、 服务模块、 工人或 节点测试 :
import { createMessagingEngine, createMessagingRepository } from "@ai-matrx/messaging";
已归档的对话
艾滋病毒/艾滋病法律(Arman, 2026-09-09 - common-docs/policies/archived-items.md: 每一个
列表覆盖一个可以存档的实体,其中包含存档过滤器,默认隐藏存档
排出一到两下就可以了 三个州,不再有:
| 价值 | 列表显示的内容 |
|---|---|
active | 只有无存档的对话(默认) |
archived | 只存档对话 |
all | 每一次谈话 |
过滤器是对读者的要求,从来不是客户端过滤器. 它的行进是
p_archived 打开 get_dm_conversations_with_details这样 hasMore和
每个计数都描述了呼叫者实际要求的行. 一个收集一切的宿主
分区 ConversationSummary.isArchived 在它自己的组件中读取服务器行
它的计数描述的是不同于屏幕上的一组.
ConversationList 已经携带了控制器.. "档案(N)"点击一下,然后返回.
伯爵来自 countArchivedConversations() 并且是 CAPPED 的 : exact: false 使“N+”,
从来没有自信的完全。 一个主楼 自己的列表读取 archiveFilter, (中文(简体) ), setArchiveFilter
财务报告和审定财务报表 archivedCount 从 useConversations()。 。 。 .
最初的状态是KNOB,而不是这个包裹的口味(根法6和该法第6条):
<MessagingProvider archiveFilter={settings.lists.archivedDefault} … >
关闭它,你得到平台默认, active。 。 。 .
数据库合同
在Matrx Main, 平台的DB公约下
| 表格计划 | communication (从来没有) public(中文(简体) ) |
| RPC 计划 | public 安全局的功能一直停留在那里 两个DIFFER,故意的。 倒塌的失败 与PGRST202 |
| 表格 | dm_conversations, (中文(简体) ), dm_conversation_participants, (中文(简体) ), dm_messages |
| 区域协调机制 | dm_get_or_create_direct_conversation, (中文(简体) ), get_dm_conversations_with_details, (中文(简体) ), get_dm_user_info, (中文(简体) ), get_dm_unread_count, (中文(简体) ), is_dm_participant |
每个名字都在一个文件里 src/core/repository.ts - 还有装满了花球的金丝雀
因此,一个静默的重命名失败了构建而不是应用.
matrx-dm 在自己的项目中发展了一个平行的计划(public.conversations 编号 : messages 编号 :
message_reactions) (中文(简体) ). 根据工作订单的R8 正好 一个犬科计划存活下来,它是
这个——有明确字形、版本、软删除和认证检查的RPC.
入境点
| 条目 | 目录 | "use client" |
|---|---|---|
@ai-matrx/messaging | 无框架核心:寄存器、存储、发包、引擎、动作注册、参考文献、AI、格式化 | 没有 ——被盖章的纯模块成为RSC内部的客户参考 |
@ai-matrx/messaging/react | 提供商,钩子,以及完整的产品UI. 再导出核心, 所以一个 React app 需要一个描述符 | 对 |
@ai-matrx/messaging/styles.css | 结构CSS. 需求 | —— 说 |
@ai-matrx/messaging/tokens.css | 默认符号值 。 导入, 然后覆盖 | —— 说 |
这是故意的 没有 /native 条目. 每架C25, 平台绑定在第一个
真实的消费者,并且总是在软件包中; 反动的原生UI, 没有 Expo 应用程序练习是
未经检验的理论 腐烂。 核心已经是平台不可知论的(在 node
测试环境没有DOM shim),所以 /native 当移动应用程序采用时, 是一个 UI 层.
许可证
麻省理工学院.