evolclaw 3.1.1 → 3.1.3

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 (51) hide show
  1. package/CHANGELOG.md +428 -0
  2. package/README.md +3 -7
  3. package/SKILLS.md +311 -0
  4. package/dist/agents/claude-runner.js +1 -1
  5. package/dist/agents/codex-runner.js +75 -19
  6. package/dist/agents/gemini-runner.js +0 -2
  7. package/dist/agents/kit-renderer.js +59 -10
  8. package/dist/aun/aid/agentmd.js +50 -27
  9. package/dist/aun/aid/client.js +5 -11
  10. package/dist/aun/aid/identity.js +32 -13
  11. package/dist/aun/aid/index.js +1 -1
  12. package/dist/aun/msg/group.js +1 -0
  13. package/dist/aun/msg/p2p.js +15 -2
  14. package/dist/aun/msg/upload.js +57 -18
  15. package/dist/aun/rpc/connection.js +3 -0
  16. package/dist/channels/aun.js +122 -48
  17. package/dist/channels/dingtalk.js +1 -0
  18. package/dist/channels/feishu.js +5 -4
  19. package/dist/channels/qqbot.js +1 -0
  20. package/dist/channels/wechat.js +1 -0
  21. package/dist/channels/wecom.js +1 -0
  22. package/dist/cli/agent.js +142 -40
  23. package/dist/cli/index.js +103 -58
  24. package/dist/cli/init-channel.js +4 -2
  25. package/dist/cli/init.js +55 -26
  26. package/dist/cli/watch-msg.js +3 -1
  27. package/dist/config-store.js +22 -1
  28. package/dist/core/channel-loader.js +4 -4
  29. package/dist/core/command-handler.js +626 -538
  30. package/dist/core/evolagent-registry.js +45 -9
  31. package/dist/core/evolagent.js +35 -4
  32. package/dist/core/message/im-renderer.js +14 -4
  33. package/dist/core/message/message-bridge.js +149 -25
  34. package/dist/core/message/message-processor.js +45 -38
  35. package/dist/core/session/session-fs-store.js +23 -0
  36. package/dist/core/session/session-manager.js +188 -42
  37. package/dist/index.js +15 -17
  38. package/dist/paths.js +35 -0
  39. package/dist/utils/cross-platform.js +2 -1
  40. package/kits/docs/INDEX.md +6 -0
  41. package/kits/eck_manifest.json +3 -3
  42. package/kits/rules/02-navigation.md +1 -0
  43. package/kits/rules/06-channel.md +2 -18
  44. package/kits/templates/system-fragments/baseagent.md +2 -2
  45. package/kits/templates/system-fragments/channel.md +18 -9
  46. package/kits/templates/system-fragments/eckruntime.md +14 -0
  47. package/kits/templates/system-fragments/identity.md +5 -6
  48. package/kits/templates/system-fragments/relation.md +7 -5
  49. package/kits/templates/system-fragments/venue.md +2 -3
  50. package/package.json +5 -2
  51. package/kits/templates/system-fragments/runtime.md +0 -19
package/dist/cli/init.js CHANGED
@@ -3,7 +3,8 @@ import readline from 'readline';
3
3
  import { resolvePaths, ensureDataDirs } from '../paths.js';
4
4
  import { commandExists } from '../utils/cross-platform.js';
5
5
  import { scanInstances } from '../utils/instance-registry.js';
6
- import { saveDefaultsSafe } from '../config-store.js';
6
+ import { saveDefaultsSafe, loadAllAgents } from '../config-store.js';
7
+ import { isCodexSdkAvailable } from '../agents/codex-runner.js';
7
8
  // ==================== Helpers ====================
8
9
  function ask(rl, question) {
9
10
  return new Promise(resolve => rl.question(question, resolve));
@@ -14,8 +15,13 @@ const BASEAGENT_ENV_KEY = {
14
15
  codex: 'OPENAI_API_KEY',
15
16
  gemini: 'GEMINI_API_KEY',
16
17
  };
18
+ function isBaseagentAvailable(baseagent) {
19
+ if (baseagent === 'codex')
20
+ return isCodexSdkAvailable();
21
+ return commandExists(baseagent);
22
+ }
17
23
  function detectAvailable() {
18
- return BASEAGENT_CANDIDATES.filter(b => commandExists(b));
24
+ return BASEAGENT_CANDIDATES.filter(isBaseagentAvailable);
19
25
  }
20
26
  function pickDefault(available) {
21
27
  return (available.includes('claude') ? 'claude' : available[0]);
@@ -45,9 +51,10 @@ export async function cmdInit(options) {
45
51
  // ── 2. 探测 baseagent ──
46
52
  const available = detectAvailable();
47
53
  if (available.length === 0) {
48
- console.log('❌ 未检测到任何 baseagent CLI,请先安装至少一款:');
49
- for (const b of BASEAGENT_CANDIDATES)
50
- console.log(` - ${b}`);
54
+ console.log('❌ 未检测到可用 baseagent。请安装至少一款:');
55
+ console.log(' - claude CLI');
56
+ console.log(' - gemini CLI');
57
+ console.log(' - optional dependency @openai/codex-sdk');
51
58
  console.log('\n安装后重新运行 evolclaw init');
52
59
  return;
53
60
  }
@@ -70,7 +77,7 @@ export async function cmdInit(options) {
70
77
  return;
71
78
  }
72
79
  if (!available.includes(options.baseagent)) {
73
- console.log(`❌ ${options.baseagent} 未在 PATH 中检测到(可用: ${available.join('/')})`);
80
+ console.log(`❌ ${options.baseagent} 当前环境不可用(可用: ${available.join('/')})`);
74
81
  return;
75
82
  }
76
83
  chosen = options.baseagent;
@@ -81,19 +88,21 @@ export async function cmdInit(options) {
81
88
  writeDefaults(defaultsPath, chosen);
82
89
  console.log(`✓ 已${exists ? '覆盖' : '创建'}: ${defaultsPath}`);
83
90
  console.log(` active_baseagent: ${chosen}`);
91
+ const { agents } = loadAllAgents();
92
+ if (agents.length === 0) {
93
+ console.log('\n提示:尚无 agent,运行以下命令创建:');
94
+ console.log(' evolclaw agent new <aid>.agentid.pub');
95
+ }
84
96
  return;
85
97
  }
86
98
  // ── 4. 交互式分支 ──
87
99
  const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
88
- try {
89
- if (exists) {
90
- const ans = (await ask(rl, `配置文件已存在: ${defaultsPath}\n 是否覆盖?[y/N] `)).trim().toLowerCase();
91
- if (ans !== 'y' && ans !== 'yes') {
92
- console.log(' 已取消');
93
- return;
94
- }
95
- }
100
+ async function askBaseagent() {
96
101
  const defaultBa = pickDefault(available);
102
+ if (available.length === 1) {
103
+ console.log(` baseagent: ${defaultBa}`);
104
+ return defaultBa;
105
+ }
97
106
  let chosen = null;
98
107
  while (chosen === null) {
99
108
  const input = (await ask(rl, `默认 baseagent (${available.join('/')}) [${defaultBa}]: `)).trim() || defaultBa;
@@ -102,22 +111,42 @@ export async function cmdInit(options) {
102
111
  continue;
103
112
  }
104
113
  if (!available.includes(input)) {
105
- console.log(` ${input} 未在 PATH 中检测到(可用: ${available.join('/')})`);
114
+ console.log(` ${input} 当前环境不可用(可用: ${available.join('/')})`);
106
115
  continue;
107
116
  }
108
117
  chosen = input;
109
118
  }
110
- writeDefaults(defaultsPath, chosen);
111
- console.log(`\n✓ 已${exists ? '覆盖' : '创建'}: ${defaultsPath}`);
112
- console.log(` active_baseagent: ${chosen}\n`);
113
- rl.close();
114
- // ── 5. 嵌套 agent new ──
115
- console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
116
- console.log('下一步:创建 agent\n');
117
- const { agentCreateInteractive } = await import('./agent.js');
118
- const result = await agentCreateInteractive();
119
- if (!result.ok) {
120
- console.error(`❌ ${result.error}`);
119
+ return chosen;
120
+ }
121
+ try {
122
+ if (exists) {
123
+ const ans = (await ask(rl, `配置文件已存在: ${defaultsPath}\n 是否覆盖?[y/N] `)).trim().toLowerCase();
124
+ if (ans === 'y' || ans === 'yes') {
125
+ const chosen = await askBaseagent();
126
+ writeDefaults(defaultsPath, chosen);
127
+ console.log(`\n✓ 已覆盖: ${defaultsPath}`);
128
+ console.log(` active_baseagent: ${chosen}\n`);
129
+ }
130
+ else {
131
+ console.log(' 已跳过(保留现有配置)\n');
132
+ }
133
+ }
134
+ else {
135
+ const chosen = await askBaseagent();
136
+ writeDefaults(defaultsPath, chosen);
137
+ console.log(`\n✓ 已创建: ${defaultsPath}`);
138
+ console.log(` active_baseagent: ${chosen}\n`);
139
+ }
140
+ // ── 5. 无 agent 时自动进入 agent new ──
141
+ const { agents } = loadAllAgents();
142
+ if (agents.length === 0) {
143
+ console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
144
+ console.log('下一步:创建 agent\n');
145
+ const { agentCreateInteractive } = await import('./agent.js');
146
+ const result = await agentCreateInteractive({ rl });
147
+ if (!result.ok) {
148
+ console.error(`❌ ${result.error}`);
149
+ }
121
150
  }
122
151
  }
123
152
  finally {
@@ -316,7 +316,9 @@ function renderMessagesPanel(state, width, height) {
316
316
  const metaTags = (m.encrypt != null || m.chatmode) ? `${MAGENTA}[${encLabel}|${modeLabel}]${RST}` : '';
317
317
  let typeTag = '';
318
318
  if (m.dir === 'out') {
319
- const source = m.source === 'cli' ? 'cli' : 'daemon';
319
+ const rawSource = m.source;
320
+ // 4 种来源: daemon | ctl | msg | cli
321
+ const source = (rawSource === 'ctl' || rawSource === 'msg' || rawSource === 'cli') ? rawSource : 'daemon';
320
322
  const method = m.msgType === 'thought' ? 'thought' : 'send';
321
323
  typeTag = `${DIM}[${source}|${method}]${RST}`;
322
324
  }
@@ -348,9 +348,30 @@ export function loadAgent(aid) {
348
348
  if (raw.aid !== aid) {
349
349
  throw new Error(`[config] ${p}: aid field "${raw.aid}" != directory name "${aid}"`);
350
350
  }
351
- return expandEnvRefs(raw);
351
+ const cfg = expandEnvRefs(raw);
352
+ if (cfg.projects?.defaultPath) {
353
+ cfg.projects.defaultPath = cfg.projects.defaultPath.replace(/[/\\]+$/, '');
354
+ }
355
+ return cfg;
352
356
  }
353
357
  export function saveAgent(value) {
358
+ if (!isValidAid(value.aid)) {
359
+ throw new Error(`[config] saveAgent: invalid aid "${value.aid}" (must be a valid multi-level domain like mybot.agentid.pub)`);
360
+ }
361
+ if (value.owners) {
362
+ for (const o of value.owners) {
363
+ if (!isValidAid(o)) {
364
+ throw new Error(`[config] saveAgent: invalid owner AID "${o}" in ${value.aid} (must be a valid multi-level domain like alice.agentid.pub)`);
365
+ }
366
+ }
367
+ }
368
+ if (value.admins) {
369
+ for (const a of value.admins) {
370
+ if (!isValidAid(a)) {
371
+ throw new Error(`[config] saveAgent: invalid admin AID "${a}" in ${value.aid} (must be a valid multi-level domain like alice.agentid.pub)`);
372
+ }
373
+ }
374
+ }
354
375
  atomicWriteJson(agentConfigPath(value.aid), value);
355
376
  }
356
377
  /**
@@ -128,18 +128,18 @@ export class ChannelLoader {
128
128
  }
129
129
  const SEP = '#';
130
130
  export function formatChannelKey(k) {
131
- return `${k.aid}${SEP}${k.type}${SEP}${k.name}`;
131
+ return `${k.type}${SEP}${encodeURIComponent(k.selfPeerId)}${SEP}${k.name}`;
132
132
  }
133
133
  export function parseChannelKey(key) {
134
134
  const parts = key.split(SEP);
135
135
  if (parts.length !== 3) {
136
136
  throw new Error(`Invalid channel key (expected 3 segments separated by '#'): ${key}`);
137
137
  }
138
- const [aid, type, name] = parts;
139
- if (!aid || !type || !name) {
138
+ const [type, encodedSelfPeerId, name] = parts;
139
+ if (!type || !encodedSelfPeerId || !name) {
140
140
  throw new Error(`Invalid channel key (empty segment): ${key}`);
141
141
  }
142
- return { aid, type, name };
142
+ return { type, selfPeerId: decodeURIComponent(encodedSelfPeerId), name };
143
143
  }
144
144
  export function tryParseChannelKey(key) {
145
145
  try {