evolclaw 3.0.0 → 3.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (104) hide show
  1. package/README.md +1 -1
  2. package/bin/ec.js +29 -0
  3. package/dist/agents/baseagent-normalize.js +19 -0
  4. package/dist/agents/claude-runner.js +47 -12
  5. package/dist/agents/codex-runner.js +2 -0
  6. package/dist/agents/gemini-runner.js +9 -9
  7. package/dist/agents/kit-renderer.js +281 -0
  8. package/dist/aun/aid/identity.js +28 -0
  9. package/dist/aun/aid/index.js +1 -1
  10. package/dist/aun/aid/lifecycle-log.js +33 -0
  11. package/dist/aun/msg/group.js +3 -1
  12. package/dist/aun/msg/p2p.js +42 -1
  13. package/dist/channels/aun.js +427 -146
  14. package/dist/channels/dingtalk.js +3 -1
  15. package/dist/channels/feishu.js +128 -7
  16. package/dist/channels/qqbot.js +3 -1
  17. package/dist/channels/wechat.js +4 -1
  18. package/dist/channels/wecom.js +3 -1
  19. package/dist/cli/bench.js +1219 -0
  20. package/dist/cli/index.js +418 -40
  21. package/dist/cli/init.js +3 -4
  22. package/dist/cli/link-rules.js +245 -0
  23. package/dist/cli/net-check.js +640 -0
  24. package/dist/cli/watch-msg.js +666 -0
  25. package/dist/config-store.js +82 -5
  26. package/dist/core/channel-loader.js +23 -10
  27. package/dist/core/command-handler.js +127 -99
  28. package/dist/core/evolagent.js +5 -10
  29. package/dist/core/message/im-renderer.js +93 -48
  30. package/dist/core/message/items-formatter.js +11 -4
  31. package/dist/core/message/message-bridge.js +11 -2
  32. package/dist/core/message/message-log.js +8 -1
  33. package/dist/core/message/message-processor.js +194 -127
  34. package/dist/core/message/message-queue.js +10 -3
  35. package/dist/core/permission.js +95 -3
  36. package/dist/core/relation/peer-identity.js +161 -0
  37. package/dist/core/session/session-manager.js +103 -65
  38. package/dist/core/trigger/manager.js +16 -0
  39. package/dist/core/trigger/parser.js +110 -0
  40. package/dist/core/trigger/scheduler.js +7 -1
  41. package/dist/data/error-dict.json +118 -0
  42. package/dist/eck/baseagent-caps.js +18 -0
  43. package/dist/eck/detect.js +47 -0
  44. package/dist/eck/init.js +77 -0
  45. package/dist/eck/rules-loader.js +28 -0
  46. package/dist/index.js +186 -19
  47. package/dist/net-check.js +640 -0
  48. package/dist/paths.js +31 -40
  49. package/dist/utils/aid-lifecycle-log.js +33 -0
  50. package/dist/utils/atomic-write.js +10 -0
  51. package/dist/utils/cross-platform.js +17 -8
  52. package/dist/utils/error-utils.js +27 -15
  53. package/dist/utils/instance-registry.js +6 -5
  54. package/dist/utils/log-writer.js +2 -1
  55. package/dist/utils/logger.js +10 -0
  56. package/dist/utils/npm-ops.js +35 -3
  57. package/dist/utils/process-introspect.js +16 -38
  58. package/dist/utils/stats.js +216 -2
  59. package/dist/watch-msg.js +26 -11
  60. package/evolclaw-install-aun.md +14 -2
  61. package/kits/docs/GUIDE.md +20 -0
  62. package/kits/docs/INDEX.md +52 -0
  63. package/kits/docs/aun/CHEATSHEET.md +17 -0
  64. package/kits/docs/aun/SYNC_PROTOCOL.md +15 -0
  65. package/kits/docs/channels/feishu.md +27 -0
  66. package/kits/docs/eck_templates/GUIDE.template.md +22 -0
  67. package/kits/docs/eck_templates/INDEX.template.md +28 -0
  68. package/kits/docs/eck_templates/path-registry.template.md +33 -0
  69. package/kits/docs/eck_templates/runtime.template.md +19 -0
  70. package/kits/docs/evolclaw/MSG_GROUP.md +30 -0
  71. package/kits/docs/evolclaw/MSG_PRIVATE.md +72 -0
  72. package/kits/docs/identity/AID_PROFILE_SPEC.md +27 -0
  73. package/kits/docs/identity/PATH_OPS.md +16 -0
  74. package/kits/docs/identity/ROLE_DETAIL.md +20 -0
  75. package/kits/docs/path-registry.md +43 -0
  76. package/kits/eck_manifest.json +95 -0
  77. package/kits/rules/01-overview.md +120 -0
  78. package/kits/rules/02-navigation.md +75 -0
  79. package/kits/rules/03-identity.md +34 -0
  80. package/kits/rules/04-relation.md +49 -0
  81. package/kits/rules/05-venue.md +45 -0
  82. package/kits/rules/06-channel.md +73 -0
  83. package/kits/templates/system-fragments/baseagent.md +2 -0
  84. package/kits/templates/system-fragments/channel.md +10 -0
  85. package/kits/templates/system-fragments/identity.md +12 -0
  86. package/kits/templates/system-fragments/relation.md +9 -0
  87. package/kits/templates/system-fragments/runtime.md +19 -0
  88. package/kits/templates/system-fragments/venue.md +5 -0
  89. package/package.json +7 -5
  90. package/dist/agents/templates.js +0 -122
  91. package/dist/data/prompts.md +0 -137
  92. package/kits/aun/meta.md +0 -25
  93. package/kits/aun/role.md +0 -25
  94. package/kits/templates/group.md +0 -20
  95. package/kits/templates/private.md +0 -9
  96. package/kits/templates/system-fragments/personal-context.md +0 -3
  97. package/kits/templates/system-fragments/self-intro.md +0 -5
  98. package/kits/templates/system-fragments/speaker-intro.md +0 -5
  99. package/kits/templates/system-fragments/venue-intro.md +0 -5
  100. /package/kits/{channels → docs/channels}/aun.md +0 -0
  101. /package/kits/{evolclaw/commands.md → docs/evolclaw/AGENT_CMD.md} +0 -0
  102. /package/kits/{evolclaw → docs/evolclaw}/self-summary.md +0 -0
  103. /package/kits/{evolclaw → docs/evolclaw}/tools.md +0 -0
  104. /package/kits/{evolclaw → docs/identity}/identity-tools.md +0 -0
@@ -1,3 +1,5 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
1
3
  export class StatsCollector {
2
4
  events = [];
3
5
  startTime;
@@ -100,6 +102,71 @@ export class StatsCollector {
100
102
  export class AidStatsCollector {
101
103
  entries = new Map();
102
104
  queueStatsProvider;
105
+ /** sessionId → 当前正在跑该 session 的 agent,task:started 写入,task:completed/error 清除 */
106
+ sessionToAgent = new Map();
107
+ constructor(eventBus) {
108
+ if (!eventBus)
109
+ return;
110
+ eventBus.subscribe('task:started', (event) => {
111
+ const e = event;
112
+ if (e.agentName)
113
+ this.onTaskStart(e.agentName, e.encrypt, e.chatmode);
114
+ if (e.agentName && e.sessionId)
115
+ this.sessionToAgent.set(e.sessionId, e.agentName);
116
+ });
117
+ eventBus.subscribe('task:completed', (event) => {
118
+ const e = event;
119
+ if (e.agentName)
120
+ this.onTaskEnd(e.agentName, 'completed', undefined, e.finalText, e.numTurns);
121
+ if (e.sessionId)
122
+ this.sessionToAgent.delete(e.sessionId);
123
+ });
124
+ eventBus.subscribe('task:error', (event) => {
125
+ const e = event;
126
+ if (e.agentName)
127
+ this.onTaskEnd(e.agentName, 'error', e.errorType);
128
+ if (e.sessionId)
129
+ this.sessionToAgent.delete(e.sessionId);
130
+ });
131
+ // thought.put 次数 + 最后一次 thought 文本
132
+ // 注意:thought.put 是 fire-and-forget async,可能在 task:completed 之后才到达,
133
+ // 所以同时累加到 currentTask(task 进行中)或 lastTaskEnd(task 已结束但 thought 属于它)
134
+ eventBus.subscribe('message:thought-put', (event) => {
135
+ const e = event;
136
+ if (!e.agentName)
137
+ return;
138
+ const entry = this.entries.get(e.agentName);
139
+ if (!entry)
140
+ return;
141
+ if (entry.currentTaskStartAt != null) {
142
+ // task 进行中
143
+ entry.currentTaskThoughtPutCount++;
144
+ if (e.text)
145
+ entry.currentTaskLastThoughtText = e.text;
146
+ }
147
+ else if (entry.lastTaskEnd) {
148
+ // task 已结束,回填到最近一次 task
149
+ entry.lastTaskEnd.thoughtPutCount++;
150
+ entry.lastTaskEnd.thoughtDuringTask = true;
151
+ if (e.text) {
152
+ const t = e.text.length > 100 ? e.text.slice(0, 100) + '…' : e.text;
153
+ entry.lastTaskEnd.lastThoughtText = t;
154
+ }
155
+ }
156
+ });
157
+ // 工具调用次数(tool:use 事件)
158
+ eventBus.subscribe('tool:use', (event) => {
159
+ const e = event;
160
+ if (!e.sessionId)
161
+ return;
162
+ const agent = this.sessionToAgent.get(e.sessionId);
163
+ if (!agent)
164
+ return;
165
+ const entry = this.entries.get(agent);
166
+ if (entry && entry.currentTaskStartAt != null)
167
+ entry.currentTaskToolUseCount++;
168
+ });
169
+ }
103
170
  setQueueStatsProvider(provider) {
104
171
  this.queueStatsProvider = provider;
105
172
  }
@@ -119,19 +186,145 @@ export class AidStatsCollector {
119
186
  lastSentAt: null,
120
187
  lastReceivedText: null,
121
188
  lastReceivedFrom: null,
189
+ lastReceivedEncrypt: null,
190
+ lastReceivedChatmode: null,
122
191
  lastSentText: null,
123
192
  lastSentTo: null,
193
+ lastSentEncrypt: null,
194
+ lastSentChatmode: null,
124
195
  uniquePeers: new Set(),
196
+ currentTaskStartAt: null,
197
+ currentTaskReplyCount: 0,
198
+ currentTaskThoughtPutCount: 0,
199
+ currentTaskToolUseCount: 0,
200
+ currentTaskNumTurns: 0,
201
+ currentTaskLastThoughtText: null,
202
+ currentTaskSessionId: null,
203
+ currentTaskChatmode: null,
204
+ currentTaskEncrypt: null,
205
+ lastTaskEnd: undefined,
125
206
  };
126
207
  this.entries.set(aid, entry);
127
208
  }
128
209
  return entry;
129
210
  }
211
+ sessionsDir;
212
+ setSessionsDir(dir) {
213
+ this.sessionsDir = dir;
214
+ }
215
+ onTaskStart(aid, encrypt, chatmode) {
216
+ const entry = this.getOrCreate(aid);
217
+ entry.currentTaskStartAt = Date.now();
218
+ entry.currentTaskReplyCount = 0;
219
+ entry.currentTaskThoughtPutCount = 0;
220
+ entry.currentTaskToolUseCount = 0;
221
+ entry.currentTaskNumTurns = 0;
222
+ entry.currentTaskLastThoughtText = null;
223
+ entry.currentTaskChatmode = chatmode ?? null;
224
+ entry.currentTaskEncrypt = encrypt ?? null;
225
+ }
226
+ onTaskEnd(aid, status, errorType, finalText, numTurns) {
227
+ const entry = this.getOrCreate(aid);
228
+ const startedAt = entry.currentTaskStartAt;
229
+ const taskEndTs = Date.now();
230
+ // 先用内存计数写入初始值(立即可用)
231
+ const buildTaskEnd = (msgCount, thoughtCount, lastThought) => ({
232
+ ts: taskEndTs,
233
+ status,
234
+ errorType,
235
+ sentDuringTask: msgCount > 0,
236
+ thoughtDuringTask: thoughtCount > 0,
237
+ lastThoughtText: lastThought,
238
+ replyCount: msgCount,
239
+ thoughtPutCount: thoughtCount,
240
+ toolUseCount: entry.currentTaskToolUseCount,
241
+ numTurns: numTurns ?? entry.currentTaskNumTurns,
242
+ finalText: finalText ? (finalText.length > 100 ? finalText.slice(0, 100) + '…' : finalText) : undefined,
243
+ chatmode: entry.currentTaskChatmode ?? undefined,
244
+ encrypt: entry.currentTaskEncrypt ?? undefined,
245
+ });
246
+ entry.lastTaskEnd = buildTaskEnd(entry.currentTaskReplyCount, entry.currentTaskThoughtPutCount, entry.currentTaskLastThoughtText ?? undefined);
247
+ // 500ms 后从 jsonl 重新统计(覆盖 thought.put 异步延迟问题)
248
+ if (this.sessionsDir && startedAt != null) {
249
+ const sessionsDir = this.sessionsDir;
250
+ const toolUseCount = entry.currentTaskToolUseCount;
251
+ const resolvedNumTurns = numTurns ?? entry.currentTaskNumTurns;
252
+ const chatmode = entry.currentTaskChatmode;
253
+ const encrypt = entry.currentTaskEncrypt;
254
+ setTimeout(() => {
255
+ try {
256
+ const { chatDirPath } = require('../core/session/session-fs-store.js');
257
+ // 找该 aid 下所有 peer 的 messages.jsonl,统计 ts >= startedAt 的出站条目
258
+ const aidDir = path.join(sessionsDir, 'aun', aid.replace(/[/%\\:*?"<>|]/g, ch => '%' + ch.charCodeAt(0).toString(16).toUpperCase().padStart(2, '0')));
259
+ if (!fs.existsSync(aidDir))
260
+ return;
261
+ let msgCount = 0, thoughtCount = 0;
262
+ let lastThoughtText;
263
+ let lastMsgText;
264
+ for (const peerDir of fs.readdirSync(aidDir, { withFileTypes: true })) {
265
+ if (!peerDir.isDirectory() || peerDir.name.startsWith('_'))
266
+ continue;
267
+ const msgFile = path.join(aidDir, peerDir.name, 'messages.jsonl');
268
+ if (!fs.existsSync(msgFile))
269
+ continue;
270
+ const lines = fs.readFileSync(msgFile, 'utf-8').split('\n').filter(Boolean);
271
+ for (const line of lines) {
272
+ try {
273
+ const rec = JSON.parse(line);
274
+ if (rec.dir !== 'out' || rec.ts < startedAt || rec.ts > taskEndTs + 2000)
275
+ continue;
276
+ if (rec.msgType === 'thought') {
277
+ thoughtCount++;
278
+ if (rec.content)
279
+ lastThoughtText = rec.content.length > 100 ? rec.content.slice(0, 100) + '…' : rec.content;
280
+ }
281
+ else if (rec.msgType === 'text') {
282
+ msgCount++;
283
+ if (rec.content)
284
+ lastMsgText = rec.content.length > 100 ? rec.content.slice(0, 100) + '…' : rec.content;
285
+ }
286
+ }
287
+ catch { }
288
+ }
289
+ }
290
+ const currentEntry = this.entries.get(aid);
291
+ if (currentEntry?.lastTaskEnd?.ts === taskEndTs) {
292
+ currentEntry.lastTaskEnd = {
293
+ ts: taskEndTs,
294
+ status,
295
+ errorType,
296
+ sentDuringTask: msgCount > 0,
297
+ thoughtDuringTask: thoughtCount > 0,
298
+ lastThoughtText: lastThoughtText,
299
+ replyCount: msgCount,
300
+ thoughtPutCount: thoughtCount,
301
+ toolUseCount,
302
+ numTurns: resolvedNumTurns,
303
+ finalText: finalText ? (finalText.length > 100 ? finalText.slice(0, 100) + '…' : finalText) : undefined,
304
+ chatmode: chatmode ?? undefined,
305
+ encrypt: encrypt ?? undefined,
306
+ };
307
+ // 更新 lastSentText 为最后一条 msg(如果有)
308
+ if (lastMsgText && msgCount > 0) {
309
+ currentEntry.lastSentText = lastMsgText;
310
+ }
311
+ }
312
+ }
313
+ catch { }
314
+ }, 500);
315
+ }
316
+ entry.currentTaskStartAt = null;
317
+ entry.currentTaskReplyCount = 0;
318
+ entry.currentTaskThoughtPutCount = 0;
319
+ entry.currentTaskToolUseCount = 0;
320
+ entry.currentTaskNumTurns = 0;
321
+ entry.currentTaskLastThoughtText = null;
322
+ }
130
323
  setSelfName(aid, name) {
131
324
  const entry = this.getOrCreate(aid);
132
325
  entry.selfName = name;
133
326
  }
134
- recordInbound(aid, fromPeer, byteLength, text, isSystem = false) {
327
+ recordInbound(aid, fromPeer, byteLength, text, isSystem = false, encrypt, chatmode) {
135
328
  const entry = this.getOrCreate(aid);
136
329
  if (isSystem) {
137
330
  entry.systemReceived++;
@@ -142,11 +335,15 @@ export class AidStatsCollector {
142
335
  entry.lastReceivedFrom = fromPeer;
143
336
  if (text)
144
337
  entry.lastReceivedText = text.length > 100 ? text.slice(0, 100) + '…' : text;
338
+ if (encrypt != null)
339
+ entry.lastReceivedEncrypt = encrypt;
340
+ if (chatmode)
341
+ entry.lastReceivedChatmode = chatmode;
145
342
  }
146
343
  entry.bytesReceived += byteLength;
147
344
  entry.uniquePeers.add(fromPeer);
148
345
  }
149
- recordOutbound(aid, toPeer, byteLength, text, isSystem = false) {
346
+ recordOutbound(aid, toPeer, byteLength, text, isSystem = false, encrypt, chatmode) {
150
347
  const entry = this.getOrCreate(aid);
151
348
  if (isSystem) {
152
349
  entry.systemSent++;
@@ -157,6 +354,18 @@ export class AidStatsCollector {
157
354
  entry.lastSentTo = toPeer;
158
355
  if (text)
159
356
  entry.lastSentText = text.length > 100 ? text.slice(0, 100) + '…' : text;
357
+ if (encrypt != null)
358
+ entry.lastSentEncrypt = encrypt;
359
+ if (chatmode)
360
+ entry.lastSentChatmode = chatmode;
361
+ // 累计当前 task 的回复数
362
+ if (entry.currentTaskStartAt != null) {
363
+ entry.currentTaskReplyCount++;
364
+ if (chatmode)
365
+ entry.currentTaskChatmode = chatmode;
366
+ if (encrypt != null)
367
+ entry.currentTaskEncrypt = encrypt;
368
+ }
160
369
  }
161
370
  entry.bytesSent += byteLength;
162
371
  entry.uniquePeers.add(toPeer);
@@ -180,11 +389,16 @@ export class AidStatsCollector {
180
389
  lastSentAt: entry.lastSentAt,
181
390
  lastReceivedText: entry.lastReceivedText,
182
391
  lastReceivedFrom: entry.lastReceivedFrom,
392
+ lastReceivedEncrypt: entry.lastReceivedEncrypt,
393
+ lastReceivedChatmode: entry.lastReceivedChatmode,
183
394
  lastSentText: entry.lastSentText,
184
395
  lastSentTo: entry.lastSentTo,
396
+ lastSentEncrypt: entry.lastSentEncrypt,
397
+ lastSentChatmode: entry.lastSentChatmode,
185
398
  uniquePeerCount: entry.uniquePeers.size,
186
399
  processing: queueStats.processing,
187
400
  queued: queueStats.queued,
401
+ lastTaskEnd: entry.lastTaskEnd,
188
402
  });
189
403
  }
190
404
  return out;
package/dist/watch-msg.js CHANGED
@@ -11,6 +11,7 @@ const CYAN = isTTY ? '\x1b[36m' : '';
11
11
  const GREEN = isTTY ? '\x1b[32m' : '';
12
12
  const BLUE = isTTY ? '\x1b[34m' : '';
13
13
  const ORANGE = isTTY ? '\x1b[38;5;208m' : '';
14
+ const BG_SEL = isTTY ? '\x1b[48;5;236m' : ''; // dark gray background for selected row
14
15
  // ==================== Helpers ====================
15
16
  function visualWidth(s) {
16
17
  const stripped = s.replace(/\x1b\[[0-9;]*m/g, '');
@@ -56,6 +57,14 @@ function formatTime(ts) {
56
57
  const d = new Date(ts);
57
58
  return `${String(d.getHours()).padStart(2, '0')}:${String(d.getMinutes()).padStart(2, '0')}`;
58
59
  }
60
+ function formatDateTime(ts) {
61
+ const d = new Date(ts);
62
+ const mo = String(d.getMonth() + 1).padStart(2, '0');
63
+ const dd = String(d.getDate()).padStart(2, '0');
64
+ const hh = String(d.getHours()).padStart(2, '0');
65
+ const mm = String(d.getMinutes()).padStart(2, '0');
66
+ return `${mo}-${dd} ${hh}:${mm}`;
67
+ }
59
68
  function shortAid(aid) {
60
69
  return aid.split('.')[0];
61
70
  }
@@ -169,10 +178,12 @@ function renderScopePanel(state, width, height) {
169
178
  const a = state.localAids[i];
170
179
  const sel = isActive && i === state.scopeIndex;
171
180
  const chosen = state.selectedLocalAid === a.aid;
172
- const marker = sel ? `${CYAN}${BOLD}▸ ` : (chosen ? `${CYAN} ` : ' ');
181
+ const bg = sel ? BG_SEL : '';
182
+ const marker = sel ? `${bg}${CYAN}${BOLD}▸ ` : (chosen ? `${CYAN} ` : ' ');
173
183
  const name = truncate(shortAid(a.aid), width - 4);
174
184
  lines.push(padRight(`${marker}${name}${RST}`, width));
175
- const stats = ` ${DIM}↓${a.totalIn} ↑${a.totalOut} peers:${a.peerCount}${RST}`;
185
+ const statsBg = sel ? BG_SEL : '';
186
+ const stats = `${statsBg} ${DIM}↓${a.totalIn} ↑${a.totalOut} peers:${a.peerCount}${RST}`;
176
187
  lines.push(padRight(stats, width));
177
188
  if (lines.length < height)
178
189
  lines.push(padRight('', width));
@@ -195,19 +206,22 @@ function renderStatsPanel(state, width, height) {
195
206
  const now = Date.now();
196
207
  // "All" item at index 0
197
208
  const allSel = isActive && state.statsIndex === 0;
198
- const allMarker = allSel ? `${CYAN}${BOLD}▸ ` : ' ';
209
+ const allBg = allSel ? BG_SEL : '';
210
+ const allMarker = allSel ? `${allBg}${CYAN}${BOLD}▸ ` : ' ';
199
211
  lines.push(padRight(`${allMarker}All (${state.peers.length} peers)${RST}`, width));
200
212
  if (lines.length < height)
201
213
  lines.push(padRight('', width));
202
214
  for (let i = 0; i < state.peers.length && lines.length < height; i++) {
203
215
  const p = state.peers[i];
204
216
  const sel = isActive && state.statsIndex === i + 1;
205
- const marker = sel ? `${CYAN}${BOLD}▸ ` : ' ';
217
+ const bg = sel ? BG_SEL : '';
218
+ const marker = sel ? `${bg}${CYAN}${BOLD}▸ ` : ' ';
206
219
  const displayName = p.peerName || shortAid(p.peerId);
207
220
  const name = truncate(displayName, width - 4);
208
221
  lines.push(padRight(`${marker}${name}${RST}`, width));
222
+ const detailBg = sel ? BG_SEL : '';
209
223
  const ago = p.lastAt ? formatTimeAgo(now - p.lastAt) : '-';
210
- const detail = ` ${DIM}↓${p.inbound} ↑${p.outbound} ${ago}${RST}`;
224
+ const detail = `${detailBg} ${DIM}↓${p.inbound} ↑${p.outbound} ${ago}${RST}`;
211
225
  lines.push(padRight(detail, width));
212
226
  if (lines.length < height)
213
227
  lines.push(padRight('', width));
@@ -231,10 +245,11 @@ function renderMessagesPanel(state, width, height) {
231
245
  const msgWidth = width - 3;
232
246
  for (let i = startIdx; i < endIdx; i++) {
233
247
  const m = msgs[i];
234
- const time = formatTime(m.ts);
248
+ const time = formatDateTime(m.ts);
235
249
  const dir = m.dir === 'in' ? `${GREEN}↓${RST}` : `${BLUE}↑${RST}`;
236
- const peer = m.dir === 'in' ? shortAid(m.from) : shortAid(m.to);
237
- const header = `${DIM}${time}${RST} ${dir} ${ORANGE}${peer}${RST}`;
250
+ const from = shortAid(m.from);
251
+ const to = shortAid(m.to);
252
+ const header = `${DIM}${time}${RST} ${dir} ${ORANGE}${from}${RST}${DIM}→${RST}${GREEN}${to}${RST}`;
238
253
  const headerLine = padRight(header, msgWidth);
239
254
  const sbIdx = lines.length - 1;
240
255
  lines.push(`${headerLine} ${scrollbar[sbIdx] || ' '}`);
@@ -255,9 +270,9 @@ function renderMessagesPanel(state, width, height) {
255
270
  function renderFrame(state) {
256
271
  const cols = process.stdout.columns || 120;
257
272
  const rows = (process.stdout.rows || 40) - 3;
258
- const leftW = Math.max(24, Math.floor(cols * 0.28));
259
- const midW = Math.max(28, Math.floor(cols * 0.30));
260
- const rightW = Math.max(30, cols - leftW - midW - 4);
273
+ const leftW = Math.max(20, Math.floor(cols * 0.20));
274
+ const midW = Math.max(24, Math.floor(cols * 0.22));
275
+ const rightW = Math.max(40, cols - leftW - midW - 4);
261
276
  const bodyHeight = rows - 2;
262
277
  const leftLines = renderScopePanel(state, leftW, bodyHeight);
263
278
  const midLines = renderStatsPanel(state, midW, bodyHeight);
@@ -19,6 +19,8 @@ EvolClaw 是一个开源的 AI Agent 网关,将 Claude/Codex 等 AI 后端连
19
19
 
20
20
  ## 安装步骤
21
21
 
22
+ > **⚠️ 给执行安装的 AI Agent**:本文档中所有破坏性命令(`evolclaw stop` / `evolclaw restart` / `rm -rf` / `evolclaw aid delete` 等)**必须先 `AskUserQuestion` 等用户明确回复后才能执行**,不要因为已经发出确认提示就默认 yes 自动跑。用户在打字的几秒内决定 "等等先别停",比事后回滚要省事得多。
23
+
22
24
  ### 步骤 1:检查环境
23
25
 
24
26
  ```bash
@@ -169,23 +171,33 @@ AUN 通道由 `agent.aid` 隐式驱动,不需要单独配 channel——`evolcl
169
171
  cat $EVOLCLAW_HOME/agents/<aid>/config.json
170
172
  ```
171
173
 
172
- 检查:
174
+ 检查关键字段:
173
175
 
174
176
  - `aid` — 你的 AID
175
177
  - `owners` — Owner AID 数组(可为空,留给自动绑定)
176
178
  - `active_baseagent` — `claude` / `codex` / `gemini`
177
179
  - `projects.defaultPath` — 项目路径(目录需存在)
178
- - `channels` — 数组,初始可为空(AUN 隐式上线)
180
+ - `channels` — 数组,初始为空(AUN 隐式上线,无需在此配置)
179
181
  - `initialized` — 应为 `false`(首次连接成功后由系统更新为 `true`)
180
182
 
183
+ > 注:config.json 还包含 `$schema_version`、`enabled`、`baseagents`、`chatmode`、`dispatch` 等字段,由 `agent new` 自动填入,无需手动检查。
184
+
181
185
  如发现缺失或异常,向用户说明并提供修复方案。
182
186
 
183
187
  ### 步骤 9:启动服务
184
188
 
189
+ 首次安装(服务未运行):
190
+
185
191
  ```bash
186
192
  evolclaw start
187
193
  ```
188
194
 
195
+ 若服务已在运行(升级/重装场景,步骤 3 已停止旧进程):
196
+
197
+ ```bash
198
+ evolclaw restart
199
+ ```
200
+
189
201
  ### 步骤 10:验证运行状态
190
202
 
191
203
  ```bash
@@ -0,0 +1,20 @@
1
+ # 文档查阅指南
2
+
3
+ ## 查阅流程
4
+
5
+ 1. 先看 `$KITS_RULES`(自动加载的 8 个规则文件)了解机制骨架
6
+ 2. 需要详细信息时,按 `INDEX.md` 找到对应文档路径
7
+ 3. Read 对应文档
8
+
9
+ ## 路径解析
10
+
11
+ 文档中用 `$名称` 引用路径。解析步骤:
12
+ 1. 查 `$KITS_RULES/01-entry.md` 的路径体系速查表
13
+ 2. 如需完整定义,Read `$KITS_DOCS/path-registry.md`
14
+ 3. 如需运行时实际值,Read `$ECK/path-registry.md`
15
+
16
+ ## 不要做的事
17
+
18
+ - 不要一次性加载所有文档——按需逐个 Read
19
+ - 不要猜测路径——查注册表
20
+ - 不要在当前会话输出对外消息——用 CLI
@@ -0,0 +1,52 @@
1
+ # EvolClaw 文档索引
2
+
3
+ 本文件是 `$KITS_DOCS` 下所有文档的索引。
4
+
5
+ ## 路径与机制
6
+
7
+ | 文档 | 路径 | 说明 |
8
+ |------|------|------|
9
+ | 查阅指南 | `GUIDE.md` | 文档查阅流程 |
10
+ | 路径定义 | `path-registry.md` | 所有预定义路径及派生规则 |
11
+
12
+ ## AUN 协议
13
+
14
+ | 文档 | 路径 | 说明 |
15
+ |------|------|------|
16
+ | 速查表 | `aun/CHEATSHEET.md` | AUN 协议命名空间与常用操作速查 |
17
+ | 同步协议 | `aun/SYNC_PROTOCOL.md` | 上游协议同步 SOP |
18
+
19
+ ## EvolClaw 命令
20
+
21
+ | 文档 | 路径 | 说明 |
22
+ |------|------|------|
23
+ | Agent 命令 | `evolclaw/AGENT_CMD.md` | agent 全生命周期管理命令 |
24
+ | 运行时工具 | `evolclaw/tools.md` | ctl 命令集 |
25
+ | 自我总结 | `evolclaw/self-summary.md` | 自我总结流程指南 |
26
+ | 私聊消息 | `evolclaw/MSG_PRIVATE.md` | 私聊消息命令 |
27
+ | 群聊消息 | `evolclaw/MSG_GROUP.md` | 群聊消息命令 |
28
+
29
+ ## 身份
30
+
31
+ | 文档 | 路径 | 说明 |
32
+ |------|------|------|
33
+ | 身份工具 | `identity/identity-tools.md` | 身份识别与环境层工具 |
34
+ | 角色详情 | `identity/ROLE_DETAIL.md` | 角色与场景详细规则 |
35
+ | AID 档案规范 | `identity/AID_PROFILE_SPEC.md` | AID 档案格式规范 |
36
+ | 路径运维 | `identity/PATH_OPS.md` | 路径运维操作 |
37
+
38
+ ## 渠道
39
+
40
+ | 文档 | 路径 | 说明 |
41
+ |------|------|------|
42
+ | AUN 渠道 | `channels/aun.md` | AUN 通信约定 |
43
+ | 飞书渠道 | `channels/feishu.md` | 飞书渠道接入 |
44
+
45
+ ## ECK 模板
46
+
47
+ | 文档 | 路径 | 说明 |
48
+ |------|------|------|
49
+ | 运行时配置模板 | `eck_templates/runtime.template.md` | agent 运行时配置模板 |
50
+ | 路径注册表模板 | `eck_templates/path-registry.template.md` | 路径实例模板 |
51
+ | 索引模板 | `eck_templates/INDEX.template.md` | agent 级索引模板 |
52
+ | 指南模板 | `eck_templates/GUIDE.template.md` | agent 级查阅指南模板 |
@@ -0,0 +1,17 @@
1
+ # AUN 协议速查表
2
+
3
+ <!-- TODO: 填充 AUN 协议命名空间与常用操作速查内容 -->
4
+
5
+ ## 命名空间
6
+
7
+ | 命名空间 | 作用 |
8
+ |---|---|
9
+ | `auth.*` | AID 创建、认证、JWT |
10
+ | `message.*` | 点对点消息收发 |
11
+ | `group.*` | 群组生命周期、群消息 |
12
+ | `storage.*` | 文件上传下载 |
13
+ | `stream.*` | 实时流(语音/视频/token) |
14
+ | `meta.*` | ping、状态、信任根查询 |
15
+ | `nameservice.*` | AID 名字注册查询 |
16
+ | `custody.*` | AID 托管 |
17
+ | `peer.*` / `relay.*` | 直连/中继认证 |
@@ -0,0 +1,15 @@
1
+ # AUN 协议同步 SOP
2
+
3
+ <!-- TODO: 填充上游协议同步流程 -->
4
+
5
+ ## 同步时机
6
+
7
+ - AUN SDK 有新版本发布时
8
+ - 协议文档有重大变更时
9
+
10
+ ## 同步步骤
11
+
12
+ 1. 拉取上游最新协议文档
13
+ 2. 对比本地认知包与上游差异
14
+ 3. 更新 `$KITS_RULES/04-aun.md` 和 `$KITS_DOCS/aun/` 下相关文档
15
+ 4. 更新索引
@@ -0,0 +1,27 @@
1
+ # 飞书渠道
2
+
3
+ <!-- TODO: 填充飞书渠道接入文档 -->
4
+
5
+ ## 概述
6
+
7
+ 飞书渠道通过 evolclaw 的 feishu channel 实现,支持:
8
+ - 单聊消息收发
9
+ - 群聊消息收发
10
+ - 合并转发消息解析
11
+ - 文件/图片/视频消息
12
+
13
+ ## 配置
14
+
15
+ 在 evolclaw 配置中启用飞书渠道:
16
+
17
+ ```json
18
+ {
19
+ "channels": {
20
+ "feishu": {
21
+ "enabled": true,
22
+ "appId": "<app-id>",
23
+ "appSecret": "<app-secret>"
24
+ }
25
+ }
26
+ }
27
+ ```
@@ -0,0 +1,22 @@
1
+ # Agent 查阅指南
2
+
3
+ 本文件是 {{SELF_AID}} 的个人查阅指南。
4
+
5
+ ## 查阅优先级
6
+
7
+ 1. `$KITS_RULES`(自动加载)— ECK 机制骨架
8
+ 2. `$AGENT_INDEX/INDEX.md`(本目录)— 个人索引
9
+ 3. `$KITS_DOCS/INDEX.md` — evolclaw 级文档索引
10
+ 4. 按索引中的路径 Read 具体文档
11
+
12
+ ## 路径解析
13
+
14
+ 遇到 `$名称` 时:
15
+ 1. 查 `$ECK/path-registry.md` 获取实际路径
16
+ 2. 如果实例文件中没有,查 `$KITS_DOCS/path-registry.md` 了解派生规则
17
+
18
+ ## 索引维护
19
+
20
+ 当以下范围内有文档变动时,更新本索引:
21
+ - `{{CURRENT_PROJECT}}`
22
+ - `{{AGENT_DIR}}`
@@ -0,0 +1,28 @@
1
+ # Agent 索引
2
+
3
+ 本文件是 {{SELF_AID}} 的 agent 级文档索引。
4
+
5
+ ## 项目文档
6
+
7
+ | 文档 | 路径 | 说明 | 最后更新 |
8
+ |------|------|------|----------|
9
+ | — | — | (初始化后由 agent 维护) | — |
10
+
11
+ ## 个人档案
12
+
13
+ | 文档 | 路径 | 说明 | 最后更新 |
14
+ |------|------|------|----------|
15
+ | 人格档案 | `$SELF_DIR/persona.md` | 行为规范与人格定义 | {{CREATED_AT}} |
16
+ | 工作记忆 | `$SELF_DIR/memory/working.md` | 短期关注点 | {{CREATED_AT}} |
17
+
18
+ ## 关系档案
19
+
20
+ | 对端 AID | 路径 | 最后交互 |
21
+ |----------|------|----------|
22
+ | — | — | — |
23
+
24
+ ## 场所档案
25
+
26
+ | 场所 | 路径 | 说明 |
27
+ |------|------|------|
28
+ | — | — | — |
@@ -0,0 +1,33 @@
1
+ # 路径注册表实例
2
+
3
+ 本文件记录运行时解析出的实际路径值。由 evolclaw 初始化时生成。
4
+
5
+ ## 基础路径
6
+
7
+ | 名称 | 实际路径 |
8
+ |------|----------|
9
+ | `$EVOLCLAW_HOME` | {{EVOLCLAW_HOME}} |
10
+ | `$PACKAGE_ROOT` | {{PACKAGE_ROOT}} |
11
+ | `$CURRENT_PROJECT` | {{CURRENT_PROJECT}} |
12
+
13
+ ## 派生路径
14
+
15
+ | 名称 | 实际路径 |
16
+ |------|----------|
17
+ | `$KITS` | {{KITS}} |
18
+ | `$KITS_RULES` | {{KITS_RULES}} |
19
+ | `$KITS_DOCS` | {{KITS_DOCS}} |
20
+ | `$KITS_TEMPLATES` | {{KITS_TEMPLATES}} |
21
+ | `$ECK` | {{ECK}} |
22
+ | `$AGENT_DIR` | {{AGENT_DIR}} |
23
+ | `$SELF_DIR` | {{SELF_DIR}} |
24
+ | `$RELATIONS_DIR` | {{RELATIONS_DIR}} |
25
+ | `$VENUES_DIR` | {{VENUES_DIR}} |
26
+ | `$AGENT_INDEX` | {{AGENT_INDEX}} |
27
+
28
+ ## 外部依赖
29
+
30
+ | 名称 | 实际路径 | 状态 |
31
+ |------|----------|------|
32
+ | `$KITE` | {{KITE}} | {{KITE_STATUS}} |
33
+ | `$AUN_SDK_CORE` | {{AUN_SDK_CORE}} | {{AUN_SDK_STATUS}} |
@@ -0,0 +1,19 @@
1
+ # 运行时配置
2
+
3
+ ## 基本信息
4
+
5
+ - AID: {{SELF_AID}}
6
+ - 名称: {{AGENT_NAME}}
7
+ - 创建时间: {{CREATED_AT}}
8
+
9
+ ## 运行时参数
10
+
11
+ - 模型: {{MODEL_ID}}
12
+ - 推理强度: {{EFFORT_LEVEL}}
13
+ - 权限模式: {{PERMISSION_MODE}}
14
+
15
+ ## 行为配置
16
+
17
+ - 自我总结: {{SELF_SUMMARY_ENABLED}}
18
+ - 空闲总结间隔: {{IDLE_SUMMARY_MINUTES}} 分钟
19
+ - 每日总结 token 预算: {{DAILY_SUMMARY_BUDGET}}