evolclaw 2.8.3 → 3.0.0

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 (105) hide show
  1. package/README.md +21 -12
  2. package/dist/agents/claude-runner.js +102 -38
  3. package/dist/agents/codex-runner.js +11 -14
  4. package/dist/agents/gemini-runner.js +10 -12
  5. package/dist/agents/resolve.js +134 -0
  6. package/dist/agents/templates.js +3 -3
  7. package/dist/aun/aid/agentmd.js +186 -0
  8. package/dist/aun/aid/client.js +134 -0
  9. package/dist/aun/aid/identity.js +131 -0
  10. package/dist/aun/aid/index.js +3 -0
  11. package/dist/aun/aid/types.js +1 -0
  12. package/dist/aun/aid/validation.js +21 -0
  13. package/dist/aun/msg/group.js +291 -0
  14. package/dist/aun/msg/index.js +4 -0
  15. package/dist/aun/msg/p2p.js +144 -0
  16. package/dist/aun/msg/payload-type.js +27 -0
  17. package/dist/aun/msg/upload.js +98 -0
  18. package/dist/aun/outbox.js +138 -0
  19. package/dist/aun/rpc/caller.js +42 -0
  20. package/dist/aun/rpc/connection.js +34 -0
  21. package/dist/aun/rpc/index.js +2 -0
  22. package/dist/aun/storage/download.js +29 -0
  23. package/dist/aun/storage/index.js +3 -0
  24. package/dist/aun/storage/manage.js +10 -0
  25. package/dist/aun/storage/upload.js +35 -0
  26. package/dist/channels/aun.js +1051 -288
  27. package/dist/channels/dingtalk.js +58 -5
  28. package/dist/channels/feishu.js +266 -30
  29. package/dist/channels/qqbot.js +67 -12
  30. package/dist/channels/wechat.js +61 -4
  31. package/dist/channels/wecom.js +58 -5
  32. package/dist/cli/agent.js +800 -0
  33. package/dist/cli/index.js +4253 -0
  34. package/dist/{utils → cli}/init-channel.js +211 -621
  35. package/dist/cli/init.js +178 -0
  36. package/dist/config-store.js +613 -0
  37. package/dist/core/{agent-loader.js → baseagent-loader.js} +6 -12
  38. package/dist/core/channel-loader.js +162 -11
  39. package/dist/core/command-handler.js +858 -847
  40. package/dist/core/evolagent-registry.js +191 -371
  41. package/dist/core/evolagent.js +203 -234
  42. package/dist/core/interaction-router.js +52 -5
  43. package/dist/core/message/im-renderer.js +480 -0
  44. package/dist/core/message/items-formatter.js +61 -0
  45. package/dist/core/message/message-bridge.js +104 -56
  46. package/dist/core/message/message-log.js +91 -0
  47. package/dist/core/message/message-processor.js +309 -142
  48. package/dist/core/message/message-queue.js +3 -3
  49. package/dist/core/permission.js +21 -8
  50. package/dist/core/session/adapters/codex-session-file-adapter.js +24 -2
  51. package/dist/core/session/session-fs-store.js +230 -0
  52. package/dist/core/session/session-manager.js +704 -775
  53. package/dist/core/session/session-mapper.js +87 -0
  54. package/dist/core/trigger/manager.js +122 -0
  55. package/dist/core/trigger/parser.js +128 -0
  56. package/dist/core/trigger/scheduler.js +224 -0
  57. package/dist/{templates → data}/prompts.md +34 -1
  58. package/dist/index.js +431 -275
  59. package/dist/ipc.js +49 -0
  60. package/dist/paths.js +82 -9
  61. package/dist/types.js +8 -2
  62. package/dist/utils/atomic-write.js +79 -0
  63. package/dist/utils/channel-helpers.js +46 -0
  64. package/dist/utils/cross-platform.js +0 -18
  65. package/dist/utils/instance-registry.js +433 -0
  66. package/dist/utils/log-writer.js +216 -0
  67. package/dist/utils/logger.js +24 -77
  68. package/dist/utils/media-cache.js +23 -0
  69. package/dist/utils/{upgrade.js → npm-ops.js} +52 -21
  70. package/dist/utils/process-introspect.js +144 -0
  71. package/dist/utils/stats.js +192 -0
  72. package/dist/watch-msg.js +529 -0
  73. package/evolclaw-install-aun.md +114 -46
  74. package/kits/aun/meta.md +25 -0
  75. package/kits/aun/role.md +25 -0
  76. package/kits/channels/aun.md +25 -0
  77. package/kits/evolclaw/commands.md +31 -0
  78. package/kits/evolclaw/identity-tools.md +26 -0
  79. package/kits/evolclaw/self-summary.md +29 -0
  80. package/kits/evolclaw/tools.md +25 -0
  81. package/kits/templates/group.md +20 -0
  82. package/kits/templates/private.md +9 -0
  83. package/kits/templates/system-fragments/personal-context.md +3 -0
  84. package/kits/templates/system-fragments/self-intro.md +5 -0
  85. package/kits/templates/system-fragments/speaker-intro.md +5 -0
  86. package/kits/templates/system-fragments/venue-intro.md +5 -0
  87. package/package.json +7 -5
  88. package/data/evolclaw.sample.json +0 -60
  89. package/dist/channels/aun-ops.js +0 -275
  90. package/dist/cli.js +0 -2178
  91. package/dist/config.js +0 -591
  92. package/dist/core/agent-registry.js +0 -450
  93. package/dist/core/evolagent-schema.js +0 -72
  94. package/dist/core/message/stream-flusher.js +0 -238
  95. package/dist/core/message/thought-emitter.js +0 -162
  96. package/dist/core/reload-hooks.js +0 -87
  97. package/dist/prompts/templates.js +0 -122
  98. package/dist/templates/skills.md +0 -66
  99. package/dist/utils/channel-fingerprint.js +0 -59
  100. package/dist/utils/error-dict.js +0 -63
  101. package/dist/utils/format.js +0 -32
  102. package/dist/utils/init.js +0 -645
  103. package/dist/utils/migrate-project.js +0 -122
  104. package/dist/utils/reload-hooks.js +0 -87
  105. package/dist/utils/stats-collector.js +0 -99
@@ -1,122 +0,0 @@
1
- import fs from 'fs';
2
- import path from 'path';
3
- import os from 'os';
4
- import { resolvePaths } from '../paths.js';
5
- import { loadConfig, saveConfig } from '../config.js';
6
- /** 将绝对路径编码为 Claude Code 的目录名格式(/ \ . 替换为 -) */
7
- function encodePath(p) {
8
- return p.replace(/[/\\\.]/g, '-');
9
- }
10
- /** 查找最新的 ~/.codex/state_*.sqlite */
11
- function findCodexDb() {
12
- const codexHome = path.join(os.homedir(), '.codex');
13
- if (!fs.existsSync(codexHome))
14
- return null;
15
- const files = fs.readdirSync(codexHome)
16
- .filter(f => /^state_\d+\.sqlite$/.test(f))
17
- .sort((a, b) => {
18
- const va = parseInt(a.match(/state_(\d+)/)?.[1] || '0');
19
- const vb = parseInt(b.match(/state_(\d+)/)?.[1] || '0');
20
- return vb - va;
21
- });
22
- return files.length > 0 ? path.join(codexHome, files[0]) : null;
23
- }
24
- export async function migrateProject(oldPath, newPath) {
25
- const result = {
26
- claudeSessionsMoved: false,
27
- claudeHistoryUpdated: false,
28
- codexUpdated: 0,
29
- evolclawDbUpdated: 0,
30
- evolclawConfigUpdated: false,
31
- directoryMoved: false,
32
- };
33
- const oldAbs = path.resolve(oldPath);
34
- const newAbs = path.resolve(newPath);
35
- if (!fs.existsSync(oldAbs))
36
- throw new Error(`源目录不存在: ${oldAbs}`);
37
- if (fs.existsSync(newAbs))
38
- throw new Error(`目标目录已存在: ${newAbs}`);
39
- const claudeProjects = path.join(os.homedir(), '.claude', 'projects');
40
- const oldEncoded = encodePath(oldAbs);
41
- const newEncoded = encodePath(newAbs);
42
- // 1. 迁移 ~/.claude/projects/{encoded}/
43
- const oldClaudeDir = path.join(claudeProjects, oldEncoded);
44
- const newClaudeDir = path.join(claudeProjects, newEncoded);
45
- if (fs.existsSync(oldClaudeDir)) {
46
- fs.renameSync(oldClaudeDir, newClaudeDir);
47
- result.claudeSessionsMoved = true;
48
- }
49
- // 2. .jsonl 内部路径不需要替换 — 它们是历史对话记录,
50
- // resume 时模型会根据当前 cwd 工作,旧路径只是历史上下文
51
- // 3. 更新 ~/.claude/history.jsonl
52
- const historyFile = path.join(os.homedir(), '.claude', 'history.jsonl');
53
- if (fs.existsSync(historyFile)) {
54
- const lines = fs.readFileSync(historyFile, 'utf-8').split('\n');
55
- const updated = lines.map(line => {
56
- if (!line.trim())
57
- return line;
58
- try {
59
- const obj = JSON.parse(line);
60
- if (obj.project === oldAbs) {
61
- obj.project = newAbs;
62
- return JSON.stringify(obj);
63
- }
64
- }
65
- catch { /* skip */ }
66
- return line;
67
- });
68
- const newContent = updated.join('\n');
69
- if (newContent !== fs.readFileSync(historyFile, 'utf-8')) {
70
- fs.writeFileSync(historyFile, newContent, 'utf-8');
71
- result.claudeHistoryUpdated = true;
72
- }
73
- }
74
- // 4. 更新 Codex SQLite threads.cwd
75
- const codexDbPath = findCodexDb();
76
- if (codexDbPath) {
77
- try {
78
- const { DatabaseSync } = await import('node:sqlite');
79
- const db = new DatabaseSync(codexDbPath);
80
- const r = db.prepare('UPDATE threads SET cwd = ? WHERE cwd = ?').run(newAbs, oldAbs);
81
- result.codexUpdated = r.changes ?? 0;
82
- db.close();
83
- }
84
- catch { /* Codex not installed or DB locked */ }
85
- }
86
- // 5. 移动项目目录
87
- fs.renameSync(oldAbs, newAbs);
88
- result.directoryMoved = true;
89
- // 6. 更新 EvolClaw sessions.db
90
- const p = resolvePaths();
91
- if (fs.existsSync(p.db)) {
92
- try {
93
- const { DatabaseSync } = await import('node:sqlite');
94
- const db = new DatabaseSync(p.db);
95
- const r = db.prepare('UPDATE sessions SET project_path = ? WHERE project_path = ?').run(newAbs, oldAbs);
96
- result.evolclawDbUpdated = r.changes ?? 0;
97
- db.close();
98
- }
99
- catch { /* DB not accessible */ }
100
- }
101
- // 7. 更新 evolclaw.json projects.list
102
- if (fs.existsSync(p.config)) {
103
- try {
104
- const config = loadConfig(p.config);
105
- if (config.projects?.list) {
106
- let changed = false;
107
- for (const [k, v] of Object.entries(config.projects.list)) {
108
- if (v === oldAbs) {
109
- config.projects.list[k] = newAbs;
110
- changed = true;
111
- }
112
- }
113
- if (changed) {
114
- saveConfig(config, p.config);
115
- result.evolclawConfigUpdated = true;
116
- }
117
- }
118
- }
119
- catch { /* config not accessible */ }
120
- }
121
- return result;
122
- }
@@ -1,87 +0,0 @@
1
- /**
2
- * Reload Hooks
3
- *
4
- * Extracted from index.ts main() for testability. Builds the ReloadHooks
5
- * implementation used by EvolAgentRegistry.reload() to drain/disconnect/start
6
- * channels during a hot reload.
7
- */
8
- import { logger } from './logger.js';
9
- export function buildReloadHooks(deps) {
10
- const { channelLoader, channelInstances, registerChannelInstance, messageQueue } = deps;
11
- const drainDelayMs = deps.drainDelayMs ?? 500;
12
- const drainTimeoutMs = deps.drainTimeoutMs ?? 30000;
13
- return {
14
- async drainChannel(channelName) {
15
- logger.info(`[Reload] Draining channel: ${channelName}`);
16
- if (messageQueue) {
17
- // Real drain: poll until empty or timeout
18
- const pollMs = 100;
19
- const start = Date.now();
20
- while (messageQueue.isChannelProcessing(channelName)) {
21
- if (Date.now() - start > drainTimeoutMs) {
22
- logger.warn(`[Reload] Drain timeout (${drainTimeoutMs}ms) for channel: ${channelName}, proceeding anyway`);
23
- return;
24
- }
25
- await new Promise(r => setTimeout(r, pollMs));
26
- }
27
- logger.info(`[Reload] Drain complete: ${channelName}`);
28
- }
29
- else if (drainDelayMs > 0) {
30
- await new Promise(r => setTimeout(r, drainDelayMs));
31
- }
32
- },
33
- async disconnectChannel(channelName) {
34
- const inst = channelInstances.find(i => i.adapter.channelName === channelName);
35
- if (!inst) {
36
- logger.warn(`[Reload] Channel ${channelName} not found, skipping disconnect`);
37
- return;
38
- }
39
- try {
40
- await inst.disconnect();
41
- const idx = channelInstances.indexOf(inst);
42
- if (idx >= 0)
43
- channelInstances.splice(idx, 1);
44
- logger.info(`[Reload] Disconnected channel: ${channelName}`);
45
- }
46
- catch (e) {
47
- logger.error(`[Reload] Failed to disconnect ${channelName}: ${e}`);
48
- throw e;
49
- }
50
- },
51
- async startChannel(agent, channelName) {
52
- const channels = agent.config.channels;
53
- let channelType = null;
54
- for (const [type, raw] of Object.entries(channels)) {
55
- const instances = Array.isArray(raw) ? raw : [raw];
56
- for (const inst of instances) {
57
- const name = inst.name ?? type;
58
- if (name === channelName) {
59
- channelType = type;
60
- break;
61
- }
62
- }
63
- if (channelType)
64
- break;
65
- }
66
- if (!channelType) {
67
- const msg = `[Reload] Channel ${channelName} not found in agent ${agent.name} config`;
68
- logger.error(msg);
69
- throw new Error(msg);
70
- }
71
- const partialConfig = {
72
- agents: agent.config.agents,
73
- channels: { [channelType]: channels[channelType] },
74
- projects: agent.config.projects,
75
- };
76
- const newInstances = await channelLoader.createAll(partialConfig);
77
- const newInst = newInstances.find(i => i.adapter.channelName === channelName);
78
- if (!newInst) {
79
- throw new Error(`[Reload] Failed to create instance ${channelName}`);
80
- }
81
- registerChannelInstance(newInst);
82
- await newInst.connect();
83
- channelInstances.push(newInst);
84
- logger.info(`[Reload] Started channel: ${channelName}`);
85
- },
86
- };
87
- }
@@ -1,99 +0,0 @@
1
- export class StatsCollector {
2
- events = [];
3
- startTime;
4
- HOUR_MS = 3_600_000;
5
- constructor(eventBus) {
6
- this.startTime = Date.now();
7
- // 订阅相关事件
8
- eventBus.subscribe('message:received', (event) => {
9
- const e = event;
10
- this.recordEvent({ type: 'received', timestamp: e.timestamp || Date.now(), agentName: e.agentName });
11
- });
12
- eventBus.subscribe('message:completed', (event) => {
13
- const e = event;
14
- this.recordEvent({ type: 'completed', timestamp: e.timestamp || Date.now(), durationMs: e.durationMs, agentName: e.agentName });
15
- });
16
- eventBus.subscribe('message:error', (event) => {
17
- const e = event;
18
- this.recordEvent({ type: 'error', timestamp: Date.now(), errorType: e.errorType, agentName: e.agentName });
19
- });
20
- eventBus.subscribe('message:interrupted', (event) => {
21
- const e = event;
22
- this.recordEvent({ type: 'interrupted', timestamp: Date.now(), agentName: e.agentName });
23
- });
24
- eventBus.subscribe('tool:result', (event) => {
25
- const e = event;
26
- if (e.isError) {
27
- this.recordEvent({ type: 'tool-error', timestamp: Date.now(), toolName: e.toolName, agentName: e.agentName });
28
- }
29
- });
30
- }
31
- recordEvent(record) {
32
- this.events.push(record);
33
- }
34
- /**
35
- * 获取统计快照。可选 agentName 过滤:未传则全局;传入则只统计该 agent。
36
- * 自动裁剪 >1h 的事件。
37
- */
38
- getSnapshot(agentName) {
39
- const now = Date.now();
40
- const cutoff = now - this.HOUR_MS;
41
- // 裁剪过期事件
42
- this.events = this.events.filter(e => e.timestamp >= cutoff);
43
- // 聚合统计(可按 agent 过滤)
44
- const filtered = agentName === undefined
45
- ? this.events
46
- : this.events.filter(e => (e.agentName ?? '[default]') === agentName);
47
- let received = 0;
48
- let completed = 0;
49
- let errors = 0;
50
- const errorsByType = {};
51
- let toolErrors = 0;
52
- const toolErrorsByName = {};
53
- let interrupts = 0;
54
- let totalDuration = 0;
55
- let durationCount = 0;
56
- for (const event of filtered) {
57
- switch (event.type) {
58
- case 'received':
59
- received++;
60
- break;
61
- case 'completed':
62
- completed++;
63
- if (event.durationMs !== undefined) {
64
- totalDuration += event.durationMs;
65
- durationCount++;
66
- }
67
- break;
68
- case 'error':
69
- errors++;
70
- if (event.errorType) {
71
- errorsByType[event.errorType] = (errorsByType[event.errorType] || 0) + 1;
72
- }
73
- break;
74
- case 'tool-error':
75
- toolErrors++;
76
- if (event.toolName) {
77
- toolErrorsByName[event.toolName] = (toolErrorsByName[event.toolName] || 0) + 1;
78
- }
79
- break;
80
- case 'interrupted':
81
- interrupts++;
82
- break;
83
- }
84
- }
85
- return {
86
- uptimeMs: now - this.startTime,
87
- lastHour: {
88
- received,
89
- completed,
90
- errors,
91
- errorsByType,
92
- toolErrors,
93
- toolErrorsByName,
94
- interrupts,
95
- avgResponseMs: durationCount > 0 ? totalDuration / durationCount : 0
96
- }
97
- };
98
- }
99
- }