lark-relay 0.2.1 → 0.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/lib/cot.js +35 -4
  2. package/package.json +1 -1
package/lib/cot.js CHANGED
@@ -26,8 +26,9 @@ const MAX_CONTENT_BYTES = 4096
26
26
  const SAY_AS = process.env.LR_COT_SAY_AS === 'reasoning' ? 'reasoning' : 'text'
27
27
 
28
28
  // claude 工具名 → COT 内置 icon 枚举。
29
- // 枚举只有 search/bash/read/write/doc/calendar/task/meeting/default,
30
- // 不在表里的一律 default —— 传非枚举值的渲染行为未验证,不赌。
29
+ // 实测 9 个枚举全部有专属图标(search 🔍 / bash </> / read 📖 / write ✏️ / doc 📄 /
30
+ // calendar 📅 / task ☑️ / meeting 📹 / default ●)。文档外的值多数回落成 default 的圆点,
31
+ // 少数(shell/edit/web)生效但只是已有图标的别名 —— 没有新增表达力且无兼容承诺,不用
31
32
  const ICONS = {
32
33
  Bash: 'bash',
33
34
  BashOutput: 'bash',
@@ -44,8 +45,26 @@ const ICONS = {
44
45
  TodoWrite: 'task',
45
46
  }
46
47
 
48
+ // MCP 工具名(mcp__<server>__<action>)按名字猜 —— 它们在 dispatch 场景很常见,
49
+ // 一律给 default 的圆点等于没信息。命名约定不保证,故只在猜中时用。
50
+ // **顺序即优先级**:域名词(doc/task/calendar)比动词更能说明工具属于哪个域,故排在前
51
+ const MCP_HINTS = [
52
+ [/meeting|minutes|\bvc\b/, 'meeting'],
53
+ [/calendar|schedule/, 'calendar'],
54
+ [/task|todo/, 'task'],
55
+ [/doc|sheet|base|wiki|file/, 'doc'],
56
+ [/write|create|update|edit|send|post|delete/, 'write'],
57
+ [/read|get|fetch|view/, 'read'],
58
+ [/search|find|query|list/, 'search'],
59
+ ]
60
+
47
61
  function iconFor(tool) {
48
- return ICONS[tool] || 'default'
62
+ if (ICONS[tool]) return ICONS[tool]
63
+ const s = String(tool).toLowerCase()
64
+ if (s.startsWith('mcp__')) {
65
+ for (const [re, icon] of MCP_HINTS) if (re.test(s)) return icon
66
+ }
67
+ return 'default'
49
68
  }
50
69
 
51
70
  // 按 UTF-8 字节截断,且不切坏字符(码点边界对齐)。
@@ -86,6 +105,7 @@ class CotSession {
86
105
  // ⚠️ 与 CardSession.seq 同名但无关:那个是服务端排序号,小了会被拒
87
106
  this.seq = 0
88
107
  this.pending = [] // 待发事件队列
108
+ this.lastStamp = 0 // 事件 timestamp 的单调下界(见 _stamp)
89
109
  this.pendingSay = null // 延迟一段的 say(见 onSay);结论不能进过程区
90
110
  this.timer = null
91
111
  // 串行队列:PUT 必须按写入顺序到达,晚发先到会让 delta 乱序拼接。
@@ -149,7 +169,18 @@ class CotSession {
149
169
  // 截不下来就丢;配对的 START/END 由 _push 的成组语义一起丢,不留空气泡
150
170
  if (Buffer.byteLength(s, 'utf8') > MAX_CONTENT_BYTES) return null
151
171
  }
152
- return { event_type: eventType, content: s, timestamp: String(Date.now()) }
172
+ return { event_type: eventType, content: s, timestamp: String(this._stamp()) }
173
+ }
174
+
175
+ // 事件 timestamp 必须**严格单调递增**。
176
+ // 实测(2026-08-20):同一批 8 个 delta 用相同 timestamp 时,客户端渲染成
177
+ // 「3 2 6 8 7 5 4 1」—— 不是反转而是完全打乱,说明它按 timestamp 排序且排序不稳定。
178
+ // 而一段 say 的 START/CONTENT/END 是同步生成的,Date.now() 必然相同 → 文字错乱。
179
+ // 墙上时间只作下界,计数器保证单调(同 paths.js 的 stamp,时钟回拨也不倒退)
180
+ _stamp() {
181
+ const now = Date.now()
182
+ this.lastStamp = now > this.lastStamp ? now : this.lastStamp + 1
183
+ return this.lastStamp
153
184
  }
154
185
 
155
186
  // 成组入队:**同一组事件保证同批发出**。
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lark-relay",
3
- "version": "0.2.1",
3
+ "version": "0.2.2",
4
4
  "description": "Lark event relay: collect events to disk, take a batch when you need it, or dispatch work to an AI agent.",
5
5
  "keywords": [
6
6
  "lark",