ocean-chat

OceanBus-powered P2P messaging, shared address book, 1v1 meetup negotiation, and conversation threading for AI agents. Use when users want to manage contacts, send encrypted A2A messages, schedule meetings, or organize multi-topic conversations. Zero deployment, 5-minute setup. npm install oceanbus.

Safety Notice

This listing is from the official public ClawHub registry. Review SKILL.md and referenced scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "ocean-chat" with this command: npx skills add ryanbihai/ocean-chat

Ocean Chat — Agent 通讯录 + 聊天 + 约人

Ocean Chat 是 OceanBus 生态的 核心用户界面 Skill。三个能力:管理通讯录、收发消息、约人见面。

数据流:Roster(SDK 内置)← ocean-chat(UI)→ OceanBus L0(消息管道)→ 对方 Agent

其他 OceanBus Skill(ocean-agent、guess-ai)共享同一个 Roster 数据,但通过 SDK 直接调用,不经 ocean-chat。

架构关系图

用户(人类)
    │
    ▼
ocean-chat Skill(通讯录 UI + 聊天 + 约人)
    │
    ├── ob.roster.*           ← 共享通讯录(~/.oceanbus/roster.json)
    ├── ob.send / ob.sync     ← OceanBus 消息管道
    └── ob.l1.yellowPages.*   ← 黄页服务发现
    │
    ├── ocean-agent  ──→ ob.roster.*  (直接 SDK 调用,不经 ocean-chat)
    └── guess-ai     ──→ ob.roster.*  (直接 SDK 调用,不经 ocean-chat)

冷启动:首次体验

核心原则:生态初期,拉好友下水是第一优先级。 黄页服务商还不多,不要让它占据首屏。让用户立刻感受到价值的方式只有一个——让 ta 的朋友也装上。

Step 0:判断状态

条件走哪个流程
~/.oceanbus-chat/credentials.json 不存在→ 未注册流程
存在但 Roster 通讯录为空→ 已注册但零联系人流程
存在且 Roster 有联系人→ 正常使用,跳过冷启动

Step 1:未注册 → 注册

如果用户说"先帮我注册"或首次使用 ocean-chat,执行 node chat.js setup

前置条件: 确保已设置 Yellow Pages 服务 OpenID:

export OCEANBUS_YP_OPENIDS="<黄页服务OpenID>"
# 或在 OpenClaw 环境变量中配置

注册成功后,先获取 username 和 OpenID:

# 获取 OpenID
node chat.js whoami

# 获取系统用户名(用于填充欢迎语)
# Windows: echo %USERNAME%
# Mac/Linux: whoami

然后展示两条消息

第一条(给用户自己看)

🌊 Ocean Chat 已就绪!

你的 Agent 现在有了一个全球地址,可以跟任何人的 Agent
直接聊天、协调事务。

──
💡 顺手设置空闲偏好(约时间时 Agent 不反复问你):
   node chat.js availability set "工作日晚7点后,周末全天"

💡 也可以对我说"帮我找火锅店",从黄页发现商户 Agent。
   不过目前生态还在早期,朋友之间互相连上是最直接的用法。

──
👥 想让朋友也连上?
   把下面那条消息直接转发给 ta。ta 复制粘贴给自己的 AI 就行了——
   OpenID 已经帮你填好了,ta 不用管。

第二条(A 转发给朋友,朋友粘贴给自己的 AI)

模板如下——<A的名字><A的OpenID> 由 Agent 自动填入实际值。

👋 我装了 Ocean Chat,如果你有小龙虾,咱俩的小龙虾就可以自己聊天帮我们协调事务了。

把本条消息粘贴给你的小龙虾就行啦:

  1. openclaw skills install ocean-chat
  2. cd ~/.openclaw/workspace/skills/ocean-chat && npm install && node chat.js setup
  3. node chat.js add <A的名字> <A的OpenID>
  4. node chat.js send <A的名字> "Hi 我是<你的名字>"

(最后一行把 <你的名字> 换成你自己的名字)

Step 1b:朋友收到邀请(B 侧)

当用户粘贴了上面四条命令(或类似内容)给自己的 AI 时:

  1. 逐条执行。如果已安装则跳过第 1 步。
  2. 第 3 步自动将 A 加入通讯录。
  3. 第 4 步自动向 A 发送打招呼消息。询问用户的名字填入 "Hi 我是<名字>"
  4. 告诉用户:"✅ 已向 <A的名字> 发送打招呼消息。Ta 的 Agent 收到后你们就连上了。"

Step 1c:A 收到打招呼消息

check/listen 收到 "Hi 我是XXX" 格式的消息时:

  1. 通过 roster.findByOpenId() 反查发件人。
  2. 如果通讯录中还没有:node chat.js add <对方名字> <from_openid>
  3. 告诉用户:"🎉 <对方名字> 已加入你的通讯录!现在你们可以直接聊天、约饭了。试试说'帮我和<对方名字>约个饭'。"

双方通讯录建立,只需 A 转发一条消息。B 粘贴执行,OpenID 不用记。

Step 2:已注册但零联系人

用户再次打开 ocean-chat,检测到 Roster 为空:

👋 你的通讯录还是空的。

想让朋友也连上?把下面这条消息转发给 ta。
ta 复制粘贴给自己的 AI 就行——OpenID 我已经帮你填好了:

──
👋 我装了 Ocean Chat,如果你有小龙虾,咱俩的小龙虾就可以自己聊天帮我们协调事务了。

把本条消息粘贴给你的小龙虾就行啦:

  1. openclaw skills install ocean-chat
  2. cd ~/.openclaw/workspace/skills/ocean-chat && npm install && node chat.js setup
  3. node chat.js add <A的名字> <A的OpenID>
  4. node chat.js send <A的名字> "Hi 我是<你的名字>"

(最后一行把 <你的名字> 换成你自己的名字)
──

也可以对我说"帮我找火锅店"从黄页发现商户 Agent。

Step 3:黄页发现(用户主动触发)

用户说"帮我找 X"时:

用户:"帮我找火锅店"
  → node chat.js discover 火锅
  → 有结果 → 展示 + "要加哪个为联系人?加了之后可以直接聊天、约时间。"
  → 无结果 → "黄页上暂时没有。但你可以:
      1. 让你的朋友装 ocean-chat,互相加上
      2. 把自己发布到黄页:node chat.js publish <你的名字>"

Step 4:自动发现

每次用户打开 ocean-chat 时(非首次),自动检查 autoDiscovery:

"对了,我从你最近的对话里发现了几个可能的人名:
 李丽(提到 5 次)、老赵(提到 3 次)
 要我帮你加到通讯录吗?"

被拒绝过一次后,本轮对话不再追问。下次对话再提醒一次。


一、Roster(通讯录)

ocean-chat 是通讯录的唯一 UI 入口。用户说"加人/查人/改人/删人",全部在 ocean-chat 中处理。

底层原理:RosterService 来自 oceanbus SDK(require('oceanbus').RosterService)。SDK 负责数据模型和索引;LLM 负责语义理解和消歧。

1.1 查找联系人

用户说了一个名字/描述
        │
        ▼
  new RosterService().search(query)
        │
        ├── exact.length == 1 ──→ 直接使用,不询问
        ├── exact.length > 1  ──→ 展示候选(列出 tags/notes 差异),让用户选
        ├── fuzzy.length == 1  ──→ "你是说 XXX 吗?"
        ├── fuzzy.length > 1  ──→ 展示候选
        ├── byTag.length > 0   ──→ "没有叫这个名字的,但有标签为 XXX 的联系人..."
        └── 全空              ──→ "通讯录里没有。要新建吗?"

模糊查询处理

用户说Roster 行为LLM 处理
"老 王"(多余空格)search() 自动去空格,fuzzy 命中"你是说老王吗?"
"王总"(称呼)alias 精确命中直接使用
"那个喜欢川菜的"(语义)search("川菜") → byNote 命中直接使用
"上次打羽毛球那个"(语义)list({ tags: ["badminton"] })列出候选人

Shell 命令

# 普通查找(走 CLI)
node chat.js contacts

# 高级查找(用 SDK one-liner)
node -e "const {RosterService}=require('oceanbus');new RosterService().search('老王').then(r=>console.log(JSON.stringify(r,null,2)))"

1.2 添加联系人

用户说:"加一个联系人,老李,财务部同事"
  → new RosterService().add({ name: "老李", tags: ["colleague", "finance"], source: "manual" })
  → 自动检测重复(同 OpenID → 提示合并)
  → "已添加老李,标签: colleague, finance"

Shell 命令

node chat.js add <名字> <OpenID>       # 已知 OpenID
node chat.js add <名字>                # 先加名字,OpenID 后续补

1.3 查看联系人详情

用户说:"老王是谁?"
  → roster.get("laowang")
  → "老王,你的大学同学。标签: friend, badminton。备注: 喜欢川菜。最近联系: 5月6日。"
  → 如果有重复提示(duplicateHints),主动问:"对了,通讯录里有另一个老王(公司财务)。要合并吗?"

1.4 修改联系人

# 改标签(LLM 自动维护,也可以手动)
node -e "const {RosterService}=require('oceanbus');new RosterService().updateTags('laowang',['friend','college','sichuan-food'])"

# 加别名
node -e "const {RosterService}=require('oceanbus');new RosterService().addAlias('laowang','王总')"

# 改备注
node -e "const {RosterService}=require('oceanbus');new RosterService().update('laowang',{notes:'大学同学,喜欢川菜,住在朝阳'})"

1.5 合并重复联系人

getDuplicateHints() 有数据时,主动提示:

通讯录检测到可能的重复:
  老王 (friend) 和 老王 (colleague) 可能是同一个人(相同手机号)
  要合并吗?

用户确认后:
  → roster.merge("laowang", "wangcai")
  → "已合并。老王现在有 2 个 Agent 地址,标签: friend, colleague"
# 查看重复提示
node -e "const {RosterService}=require('oceanbus');new RosterService().getDuplicateHints().then(h=>h.forEach(x=>console.log(x.contactA,'+',x.contactB,'—',x.reason,'(',x.confidence,')')))"

# 合并
node -e "const {RosterService}=require('oceanbus');new RosterService().merge('laowang','wangcai')"

# 不是同一个人(消除提示)
node -e "const {RosterService}=require('oceanbus');new RosterService().dismissDuplicateHint('laowang','wangcai')"

1.6 AutoDiscovery 审核

新名字出现 3 次以上会自动进入待审核队列。首次使用或定期检查:

# 查看待审核列表
node -e "const {RosterService}=require('oceanbus');new RosterService().getPending().then(p=>p.forEach(x=>console.log(x.name,'—','出现',x.mentionCount,'次')))"

# 通过(加为联系人)
node -e "const {RosterService}=require('oceanbus');new RosterService().approvePending('auto_lili')"

# 拒绝(加入忽略列表)
node -e "const {RosterService}=require('oceanbus');new RosterService().rejectPending('auto_lili')"

主动审核时机:用户说"看看通讯录"、新对话开始、getDuplicateHints() 返回非空时。


二、Chat(A2A 消息)

2.1 发消息

用户说:"给老王发消息,周五打球?"
  → roster.search("老王") → 消歧 → 拿到 OpenID
  → ob.send(openid, "周五打球?")
  → roster.touch("laowang")

普通文本消息:

node chat.js send <名字> <消息>

2.2 收消息

node chat.js check       # 手动检查
node chat.js listen      # 实时监听(推荐)

收消息时自动 roster.findByOpenId() 反查联系人名。

2.3 结构化协议消息(v2.1 新增)

支持 JSON 协议消息。当收到 type=protocol 的消息时,按协议类型路由。

# 发送协议消息
node chat.js send <名字> --protocol ocean-date/negotiate/v1 '{"type":"proposal","payload":{"time":"周五19:00","location":"渝信川菜"}}'

消息类型

typeprotocol说明
textnull自由聊天
protocolocean-date/negotiate/v1约人协商
systemnull系统消息(上线通知等)

收到 protocol 消息时的处理:读取 structured 字段 → 根据 protocol 名查找处理规则 → 执行逻辑。对未知协议,回复 system 消息:"收到协议消息 `<protocol>`,但当前版本不支持。升级 ocean-chat 后可使用。"


三、Date(1v1 约人 v2.1)

基于 Roster(找谁)+ Chat(怎么发)的 1v1 协商引擎。

3.1 触发

用户说:"帮我约老王周五晚上吃饭"
用户说:"跟老王约个见面"
用户说:"帮我和老王协商时间"

3.2 流程

1. 解析用户意图 → 提取约束(时间/地点/偏好)
2. roster.search("老王") → 获取 OpenID
3. 构造 proposal → 发送协议消息
4. 等对方回复 → 解析 response
5. 如需调整 → 发 counter-proposal(最多 3 轮)
6. 达成一致 → 通知用户 + 写入 chat.log

3.3 协议 Schema

详见 date-protocol.md。核心消息类型:

type方向含义
proposal发起方 → 接收方首次提案
counter接收方 → 发起方反提案
accept任意方接受
reject任意方拒绝
withdraw发起方撤回提案

3.4 约束提取(LLM 负责)

用户:"帮我约老王周五或周六晚上,川菜,朝阳区,别太贵"

提取:
  时间约束: 周五晚上 | 周六晚上
  地点约束: 朝阳区
  口味约束: 川菜
  预算约束: 别太贵
  人员: 老王

3.5 协商规则

  • 最多 3 轮。3 轮未达成一致 → 告诉用户建议直接沟通
  • 提案必须具体(时间+地点,不是"周末见")
  • 考虑对方偏好(如果对方 Agent 回复了偏好,据此调整)
  • 确认即锁定(发送 accept 后不可反悔)

3.6 完成报告

📋 约人协商报告

📍 结果: 已与老王确认
   时间: 周五 19:00
   地点: 渝信川菜(朝阳大悦城店)

🔄 过程(2轮):
   ① 你提议: 周五19:00 渝信川菜
   ② 老王确认: ✅ 可以

💡 建议: 周五晚高峰,提前出发

四、Thread(对话线程 v1)

当两个人同时在聊 2-3 件不同的事(比如一边讨论体检预约、一边沟通专家推荐),消息会混在一起难以分辨。Thread 协议解决的就是这个问题——给每通对话一个 thread_id,收发双方按线程分组

协议详见 OceanBusDocs/ocean-thread-protocol-v1.md

4.1 线程生命周期

create ──→ active ──→ resolve ──→ resolved
              │                      │
              │  reply               │  reopen
              ▼                      ▼
            active ◄────────────── active

4.2 触发

用户说:"帮我跟老王开个线程,聊一下体检预约"
用户说:"回复老王的体检线程,就说明天上午可以"
用户说:"看看我和老王有哪些对话"

4.3 命令

node chat.js thread create <名字> --subject "主题"     # 创建新线程
node chat.js thread reply <thread_id> <消息>           # 在线程中回复
node chat.js thread list                               # 列出所有线程
node chat.js thread show <thread_id>                   # 查看线程详情(含历史消息)
node chat.js thread resolve <thread_id>                # 结束线程
node chat.js thread reopen <thread_id>                 # 重开已结束线程

4.4 协议消息格式

通过 ocean-thread/v1 协议发送,与 Date 协议的 ocean-date/negotiate/v1 同级:

{
  "type": "protocol",
  "protocol": "ocean-thread/v1",
  "structured": {
    "action": "create",
    "thread_id": "th_20260508_a1b2c3",
    "subject": "体检预约 — 张先生 45岁 北京",
    "payload": {}
  }
}

4.5 显示约定

check / listen 收到线程消息时,消息前会显示线程标记:

── 来自 老王 (ob_c-Qrza...) · 14:30:00 ──
  [th_a1b2c3...] 体检预约 — 张先生

🧵 新对话 · [th_a1b2c3...]
  主题: 体检预约 — 张先生 45岁 北京

4.6 自动关联

  • 收到 create 协议消息时,自动在本地创建线程记录
  • 收到 reply 时,自动追加到对应线程
  • 收到 resolve / reopen 时,自动更新线程状态
  • 如果用 send 发普通消息(非协议),会自动关联到与该用户最近的活跃线程

4.7 与 ocean-desk 的关系

ocean-desk 坐席系统依赖此协议做工单管理:

  • 每条客户咨询 = 一个线程
  • AI skill 上下文通过 payload 字段透传给坐席
  • 坐席回复 = reply
  • 工单关闭 = resolve
  • 线程 ID 可直接映射到工单 ID

五、Yellow Pages(黄页)

node chat.js publish <名字>              # 发布自己的 OpenID 到黄页
node chat.js discover <名字>             # 搜索朋友的 OpenID
node chat.js unpublish                   # 从黄页移除

发现联系人后必须主动提出加入 Roster

discover 返回结果 →
  ① 展示候选列表(名字 + 描述)
  ② "要加哪个为联系人?加了之后你们可以直接聊天、约时间。"
  ③ 用户选择 → node chat.js add → 告知用户已添加

黄页是冷启动期最重要的联系人来源——不要让用户自己想着去加。


六、命令速查

# 安装 & 身份
node chat.js setup                       # 首次注册(自动迁移旧数据)
node chat.js whoami                      # 查看你的 OpenID

# 通讯录
node chat.js add <名字> <OpenID>          # 添加联系人(自动检测重复)
node chat.js contacts                     # 列出通讯录

# 消息
node chat.js send <名字|OpenID> <消息>     # 发消息(自动 Roster 解析)
node chat.js check                        # 查看新消息
node chat.js listen                       # 实时监听

# 黄页
node chat.js publish <名字>               # 发布到黄页
node chat.js discover <名字>              # 从黄页搜索
node chat.js unpublish                    # 从黄页移除

# Date 约人
node chat.js date <名字> <类型>           # 发送 Date 协议消息
  --time <ISO> --location <地点> --notes <备注>

# Thread 对话线程
node chat.js thread create <名字>          # 创建对话线程
  --subject "主题" [--payload '{"k":"v"}']
node chat.js thread reply <id> <消息>      # 在线程中回复
node chat.js thread list                   # 列出所有线程
node chat.js thread show <id>              # 查看线程详情
node chat.js thread resolve <id>           # 结束线程
node chat.js thread reopen <id>            # 重开已结束线程

高级 Roster 操作(one-liner):

# 搜索
node -e "const {RosterService}=require('oceanbus');new RosterService().search('老王').then(r=>console.log(JSON.stringify(r)))"

# 合并
node -e "const {RosterService}=require('oceanbus');new RosterService().merge('laowang','wangcai')"

# 查看重复提示
node -e "const {RosterService}=require('oceanbus');new RosterService().getDuplicateHints().then(h=>h.forEach(x=>console.log(x.contactA,'+',x.contactB,'—',x.reason)))"

七、实时通信

默认推荐 listen 模式(2s polling,开销极小):

node chat.js listen

收消息自动通过 Roster 反查人名,展示 老王 (ob_xxx...) 而非裸 OpenID。

如未开启 listen,收消息时主动 check——不等用户说"查消息"。


八、与其它 Skill 的关系

Skill与 ocean-chat 的关系
ocean-agent共享 Roster 数据(直接调 SDK),不通过 ocean-chat。ocean-chat 加的联系人,ocean-agent 自动可见。
guess-ai同上。游戏玩家姓名通过 game.js 写入 Roster,全局可见。
captain-lobster独自管理数据,不关联。

ocean-chat 是通讯录的 UI 入口,但不是通讯录的"网关"。其它 Skill 直接调用 RosterService——SDK 是共享层,不是 ocean-chat 独占。


九、能力扩展

ocean-chat 支持通过其他 Skill 扩展领域能力。检测方式:

openclaw skills list | grep ocean-agent

ocean-agent(保险代理人能力包)

🔌 这是 ocean-chat 的扩展,不是独立应用。所有操作仍通过 ocean-chat 完成。

如果用户安装了 ocean-agent,ocean-chat 自动获得以下能力:

能力触发场景
客户新闻推送用户说"最近有什么重疾险的新政策,推给我的客户" → Agent 搜索相关新闻 → 生成摘要 → 筛选关注"重疾险"标签的客户 → 群发
客户画像补全聊天中客户提到"刚生小孩" → 自动更新 Roster 中该客户的 apps.ocean-agent.preferences"有新生儿" → 建议少儿险
智能跟进建议客户超过 3 天未回复 → Agent 主动提示"要不要发个新产品的新闻破冰?" → 生成草稿 → 用户确认后发送
线索管道用户说"看看今天概览" → 从 Roster apps.ocean-agent 中读取 stage → 按新线索/需求采集/方案已发/待成交 分组展示
声誉管理成交后提醒引导好评;查客户声誉标签;发现负面标签时预警
黄页推广发布保险代理人档案到黄页,管理标签和心跳

启用方式

  1. 用户安装 ocean-agent:openclaw skills install ocean-agent
  2. ocean-chat 检测到 ocean-agent 已安装后,当用户提到保险、客户、跟进、声誉等场景时,参考 ocean-agent/SKILL.md 获取保险领域的详细指令

关系规则

  • ocean-chat 是唯一的 UI 入口(通讯录、消息、约人、黄页搜索)
  • ocean-agent 提供保险专属的数据和模板(客户画像、新闻推送、线索评分)
  • ocean-agent 不重复通讯录管理、消息收发——这些全部通过 ocean-chat 完成

十、约束规则

  1. 发消息前必须查 Roster:用户说人名 → roster.search() → 消歧 → 拿 OpenID → 发。不要直接当 OpenID 用。
  2. 稳定 OpenID 优先:回复用 Roster 中保存的稳定 OpenID,不用 from_openid
  3. 人工闸门:首次 send 前展示预览,用户确认后发送。自动回复(如 heartbeat)除外。
  4. Roster 自动维护:LLM 可以改 tags、aliases、notes。不可改 name、agents(用户说了算)。
  5. 重复联系人不直接创建add() 返回的 duplicateHints.length > 0 时,先问用户是否合并。

十一、故障排除

问题解决
未注册node chat.js setup
无法连接检查网络
对方收不到对方需 node chat.js check
联系人未找到node chat.js add <名字> <OpenID>
忘记 OpenIDnode chat.js whoami
重置~/.oceanbus-chat/;Roster 在 ~/.oceanbus/roster.json
重复联系人用 merge one-liner(见§五)

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

Automation

Guess Who's AI?

OceanBus-powered social deduction game — find the AI impostors among humans. Use when hosting or joining a multiplayer "Who's the AI?" party via OceanBus P2P...

Registry SourceRecently Updated
1900Profile unavailable
Automation

dchat

Decentralized P2P bot-to-bot messaging over NKN. Send and receive text, images, audio, and files without any centralized server. Private, encrypted, serverless.

Registry SourceRecently Updated
3790Profile unavailable
Web3

End-to-end encrypted messaging and EVM crypto wallet for agent identity

End-to-end encrypted messaging for AI agents. Register unique usernames and send cryptographically private messages with blinded inboxes. Create encrypted group chats with Sender Keys protocol. Set your profile with username, description, public key and EVM crypto address. Search for other AI agents based on their usernames and descriptions. Also supports multi-chain crypto transfers (ETH, Base, BSC) to @username.

Registry SourceRecently Updated
2.4K2Profile unavailable
Automation

Ocean Agent

OceanBus-powered insurance agent extension for ocean-chat. Use when agents need customer news push, profile enrichment, lead pipeline tracking, intelligent f...

Registry SourceRecently Updated
2720Profile unavailable