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,238 +0,0 @@
1
- import { logger } from '../../utils/logger.js';
2
- import fs from 'fs';
3
- import path from 'path';
4
- import { resolvePaths } from '../../paths.js';
5
- // 诊断日志(按需启用,通过 config.debug.flusherDiag 控制)
6
- let diagStream = null;
7
- function getDiagStream() {
8
- if (!diagStream) {
9
- const logDir = resolvePaths().logs;
10
- diagStream = fs.createWriteStream(path.join(logDir, 'flusher-diag.log'), { flags: 'a' });
11
- }
12
- return diagStream;
13
- }
14
- function diag(instanceId, action, meta = {}) {
15
- const line = JSON.stringify({ ts: new Date().toISOString(), id: instanceId, action, ...meta });
16
- getDiagStream().write(line + '\n');
17
- }
18
- /**
19
- * 流式输出缓冲器
20
- * 按时间窗口批量推送文本和活动事件
21
- *
22
- * 延迟策略:
23
- * - 第1次:立即发送(0ms)
24
- * - 第2-4次:半延迟(interval / 2)
25
- * - 第5次起:动态自适应延迟
26
- * - 计算最近10条消息的平均间隔
27
- * - 动态延迟 = 平均间隔 * 3
28
- * - 下限:interval(额定值)
29
- * - 上限:interval * 2.5
30
- */
31
- let instanceCounter = 0;
32
- export class StreamFlusher {
33
- send;
34
- interval;
35
- silent;
36
- buffer = '';
37
- queue = []; // 按入队顺序记录 activity 和 text 段
38
- timer;
39
- lastFlush = Date.now();
40
- allText = '';
41
- sentContent = false;
42
- fileMarkerPattern;
43
- flushCount = 0;
44
- messageTimestamps = [];
45
- instanceId;
46
- createTime = Date.now();
47
- diagEnabled;
48
- sendChain = Promise.resolve(); // 串行发送队列,保证消息按序到达
49
- constructor(send, interval = 4000, fileMarkerPattern, diagEnabled = false, silent = false) {
50
- this.send = send;
51
- this.interval = interval;
52
- this.silent = silent;
53
- this.fileMarkerPattern = fileMarkerPattern;
54
- this.diagEnabled = diagEnabled;
55
- this.instanceId = `F${++instanceCounter}`;
56
- if (this.diagEnabled)
57
- diag(this.instanceId, 'created', { interval, silent });
58
- }
59
- addText(text) {
60
- if (this.buffer.length === 0 && text.length > 0) {
61
- this.queue.push({ kind: 'text' });
62
- }
63
- this.buffer += text;
64
- this.allText += text;
65
- this.messageTimestamps.push(Date.now());
66
- if (this.diagEnabled)
67
- diag(this.instanceId, 'addText', { len: text.length, preview: text.substring(0, 60), bufLen: this.buffer.length, queueLen: this.queue.length });
68
- this.scheduleFlush();
69
- }
70
- addTextBlock(text) {
71
- if (this.buffer && !this.buffer.endsWith('\n')) {
72
- this.buffer += '\n\n';
73
- this.allText += '\n\n';
74
- }
75
- this.buffer += text;
76
- this.allText += text;
77
- this.queue.push({ kind: 'text' });
78
- this.messageTimestamps.push(Date.now());
79
- if (this.diagEnabled)
80
- diag(this.instanceId, 'addTextBlock', { len: text.length, preview: text.substring(0, 60), bufLen: this.buffer.length });
81
- this.scheduleFlush();
82
- }
83
- addActivity(desc) {
84
- this.queue.push({ kind: 'activity', text: desc });
85
- this.messageTimestamps.push(Date.now());
86
- if (this.diagEnabled)
87
- diag(this.instanceId, 'addActivity', { desc: desc.substring(0, 80), queueLen: this.queue.length });
88
- this.scheduleFlush();
89
- }
90
- hasContent() {
91
- return this.buffer.length > 0 || this.queue.some(e => e.kind === 'activity');
92
- }
93
- hasSentContent() {
94
- return this.sentContent;
95
- }
96
- getFinalText() {
97
- return this.allText;
98
- }
99
- getRemainingText() {
100
- return this.buffer;
101
- }
102
- stripFromBuffer(pattern) {
103
- this.buffer = this.buffer.replace(pattern, '').trim();
104
- }
105
- scheduleFlush() {
106
- if (this.silent)
107
- return; // proactive 模式:不调度发送
108
- if (this.timer) {
109
- if (this.diagEnabled)
110
- diag(this.instanceId, 'scheduleFlush:skip', { reason: 'timer_exists' });
111
- return;
112
- }
113
- let targetDelay;
114
- if (this.flushCount === 0) {
115
- targetDelay = 500;
116
- }
117
- else if (this.flushCount <= 3) {
118
- targetDelay = Math.ceil(this.interval / 2);
119
- }
120
- else if (this.messageTimestamps.length >= 5) {
121
- targetDelay = this.calculateDynamicDelay();
122
- }
123
- else {
124
- targetDelay = this.interval;
125
- }
126
- const elapsed = Date.now() - this.lastFlush;
127
- const delay = Math.max(0, targetDelay - elapsed);
128
- if (this.diagEnabled)
129
- diag(this.instanceId, 'scheduleFlush:set', { flushCount: this.flushCount, targetDelay, elapsed, actualDelay: delay });
130
- this.timer = setTimeout(() => this.flush(), delay);
131
- }
132
- calculateDynamicDelay() {
133
- const recent = this.messageTimestamps.slice(-10);
134
- const intervals = [];
135
- for (let i = 1; i < recent.length; i++) {
136
- intervals.push(recent[i] - recent[i - 1]);
137
- }
138
- if (intervals.length === 0)
139
- return this.interval;
140
- const avgInterval = intervals.reduce((a, b) => a + b) / intervals.length;
141
- let dynamicDelay = avgInterval * 3;
142
- const minDelay = this.interval;
143
- const maxDelay = this.interval * 2.5;
144
- return Math.max(minDelay, Math.min(maxDelay, dynamicDelay));
145
- }
146
- /**
147
- * 只 flush activities,保留 text buffer 不动
148
- * 用于 complete 事件前清空 pending activities,让最终文本留给 flush(true) 发送
149
- */
150
- async flushActivitiesOnly() {
151
- if (this.silent)
152
- return;
153
- const hasActivities = this.queue.some(e => e.kind === 'activity');
154
- if (!hasActivities)
155
- return;
156
- if (this.timer) {
157
- clearTimeout(this.timer);
158
- this.timer = undefined;
159
- }
160
- // 只取 activity 条目,保留 text 条目在 queue 中
161
- const activities = this.queue.filter(e => e.kind === 'activity');
162
- this.queue = this.queue.filter(e => e.kind === 'text');
163
- let output = activities.map(e => e.text).join('\n') + '\n\n';
164
- if (output && this.fileMarkerPattern) {
165
- output = output.replace(this.fileMarkerPattern, '').trim();
166
- }
167
- if (this.diagEnabled)
168
- diag(this.instanceId, 'flushActivitiesOnly', { outputLen: output.length });
169
- if (output) {
170
- this.sentContent = true; // 同步标记,避免 timer flush 未 await 时的竞态
171
- const text = output;
172
- // chain 保持不断裂:单条失败不阻塞后续(catch → resolve)
173
- this.sendChain = this.sendChain
174
- .then(() => this.send(text, false, false))
175
- .catch(e => { logger.warn('[StreamFlusher] send failed:', e); });
176
- await this.sendChain;
177
- this.lastFlush = Date.now();
178
- this.flushCount++;
179
- }
180
- }
181
- async flush(isFinal) {
182
- if (this.silent) {
183
- // 清理内部状态,避免后续误用
184
- if (this.timer) {
185
- clearTimeout(this.timer);
186
- this.timer = undefined;
187
- }
188
- this.queue = [];
189
- this.buffer = '';
190
- return;
191
- }
192
- if (this.timer) {
193
- clearTimeout(this.timer);
194
- this.timer = undefined;
195
- }
196
- let output = '';
197
- const hasText = this.buffer.length > 0;
198
- // 按入队顺序合并:遇到 text 条目时插入 buffer 内容,遇到 activity 直接追加
199
- let textInserted = false;
200
- for (const entry of this.queue) {
201
- if (entry.kind === 'activity') {
202
- // 确保 activity 前有换行分隔(text 末尾可能没有换行)
203
- if (output && !output.endsWith('\n'))
204
- output += '\n';
205
- output += entry.text + '\n';
206
- }
207
- else if (!textInserted) {
208
- if (output)
209
- output += output.endsWith('\n') ? '\n' : '\n\n';
210
- output += this.buffer;
211
- textInserted = true;
212
- }
213
- }
214
- // 如果 queue 为空但有 buffer(纯文本情况)
215
- if (!textInserted && hasText) {
216
- output += this.buffer;
217
- }
218
- this.queue = [];
219
- this.buffer = '';
220
- if (output && this.fileMarkerPattern) {
221
- output = output.replace(this.fileMarkerPattern, '').trim();
222
- }
223
- if (this.diagEnabled)
224
- diag(this.instanceId, 'flush', { isFinal, outputLen: output.length, flushCount: this.flushCount, sinceLastFlush: Date.now() - this.lastFlush, preview: output.substring(0, 80) });
225
- if (output) {
226
- this.sentContent = true; // 同步标记,避免 timer flush 未 await 时的竞态
227
- const text = output;
228
- const final = isFinal;
229
- const ht = hasText;
230
- this.sendChain = this.sendChain
231
- .then(() => this.send(text, final, ht))
232
- .catch(e => { logger.warn('[StreamFlusher] send failed:', e); });
233
- await this.sendChain;
234
- this.lastFlush = Date.now();
235
- this.flushCount++;
236
- }
237
- }
238
- }
@@ -1,162 +0,0 @@
1
- import { logger } from '../../utils/logger.js';
2
- /**
3
- * ThoughtEmitter — 将 Proactive 模式下的流式 AgentEvent 实时发送为 thought
4
- *
5
- * 设计特点:
6
- * - 不做聚合/batching,逐事件调用 adapter.putThought()
7
- * - 不感知 group vs P2P,通道差异由 adapter 内部处理
8
- * - taskId 映射为 context: { type: 'task', id: taskId }(协议 selector)
9
- * 同时写入 payload.task_id / payload.chatmode,与 message.send/group.send 保持一致
10
- * - fire-and-forget:调用方不 await emit(),错误被内部捕获
11
- */
12
- export class ThoughtEmitter {
13
- adapter;
14
- channelId;
15
- taskId;
16
- chatmode;
17
- replyContext;
18
- hasEmittedText = false;
19
- constructor(adapter, channelId, taskId, chatmode = 'proactive', replyContext) {
20
- if (!taskId) {
21
- throw new Error('[ThoughtEmitter] taskId is required at construction');
22
- }
23
- this.adapter = adapter;
24
- this.channelId = channelId;
25
- this.taskId = taskId;
26
- this.chatmode = chatmode;
27
- this.replyContext = replyContext;
28
- logger.info(`[ThoughtEmitter] created channel=${channelId} task=${taskId} chatmode=${chatmode}`);
29
- }
30
- async emit(event) {
31
- // 对齐 interactive 的 dedup:流式 text 已推过时,complete.result 不再重复发 summary
32
- if (event.type === 'complete' &&
33
- !event.isError &&
34
- event.result &&
35
- this.hasEmittedText) {
36
- return;
37
- }
38
- const payload = this.mapEventToPayload(event);
39
- if (!payload)
40
- return;
41
- if (!this.adapter.putThought)
42
- return;
43
- if (payload.stage === 'thinking') {
44
- this.hasEmittedText = true;
45
- }
46
- // payload 也带上 task_id / chatmode(与 message.send/group.send 对齐)
47
- payload.task_id = this.taskId;
48
- payload.chatmode = this.chatmode;
49
- try {
50
- await this.adapter.putThought(this.channelId, this.taskId, payload, this.replyContext);
51
- }
52
- catch (err) {
53
- logger.debug(`[ThoughtEmitter] putThought failed: ${err.message}`);
54
- }
55
- }
56
- mapEventToPayload(event) {
57
- switch (event.type) {
58
- case 'text':
59
- if (!event.text)
60
- return null;
61
- return { type: 'thought', text: event.text, stage: 'thinking' };
62
- case 'tool_use': {
63
- const desc = this.summarizeInput(event.input, event.name);
64
- return {
65
- type: 'thought',
66
- text: desc ? `🔧 ${event.name}: ${desc}` : `🔧 ${event.name}`,
67
- stage: 'tool',
68
- metadata: { tool: event.name, input: desc },
69
- };
70
- }
71
- case 'tool_result':
72
- if (event.isError) {
73
- return {
74
- type: 'thought',
75
- text: `⚠️ ${event.name}: ${event.error || '执行失败'}`,
76
- stage: 'tool',
77
- metadata: { tool: event.name, ok: false },
78
- };
79
- }
80
- {
81
- const resultText = this.truncate(this.stringifyResult(event.result), 200);
82
- return {
83
- type: 'thought',
84
- text: resultText ? `✅ ${event.name}: ${resultText}` : `✅ ${event.name}`,
85
- stage: 'tool',
86
- metadata: { tool: event.name, ok: true },
87
- };
88
- }
89
- case 'compact':
90
- return {
91
- type: 'thought',
92
- text: `💡 会话压缩完成 (压缩前 tokens: ${event.preTokens})`,
93
- stage: 'system',
94
- };
95
- case 'task_progress': {
96
- const stats = this.formatTaskStats(event);
97
- const text = event.summary
98
- ? `⏳ 子任务: ${event.summary}${stats ? ` (${stats})` : ''}`
99
- : `⏳ 子任务进行中${stats ? `: ${stats}` : ''}`;
100
- return { type: 'thought', text, stage: 'planning' };
101
- }
102
- case 'error':
103
- return { type: 'thought', text: `❌ ${event.error}`, stage: 'error' };
104
- case 'complete':
105
- if (event.isError) {
106
- const errText = event.errors?.join('; ') || event.result || '任务失败';
107
- return { type: 'thought', text: `❌ ${errText}`, stage: 'error' };
108
- }
109
- if (event.result) {
110
- return { type: 'thought', text: event.result, stage: 'summary' };
111
- }
112
- return null;
113
- case 'session_id':
114
- case 'state_changed':
115
- case 'status':
116
- return null;
117
- default:
118
- return null;
119
- }
120
- }
121
- summarizeInput(input, toolName) {
122
- if (!input || typeof input !== 'object')
123
- return '';
124
- // Bash + ctl send/file: 显示完整命令内容(含发送的消息正文)
125
- if (toolName === 'Bash' && typeof input.command === 'string') {
126
- const cmd = input.command;
127
- if (cmd.includes('evolclaw ctl send') || cmd.includes('evolclaw ctl file')) {
128
- return cmd;
129
- }
130
- }
131
- return (input.description ||
132
- input.file_path ||
133
- input.pattern ||
134
- (typeof input.command === 'string' ? input.command.substring(0, 80) : '') ||
135
- (typeof input.prompt === 'string' ? input.prompt.substring(0, 80) : '') ||
136
- (typeof input.query === 'string' ? input.query.substring(0, 80) : '') ||
137
- '');
138
- }
139
- stringifyResult(result) {
140
- if (result === null || result === undefined)
141
- return '';
142
- if (typeof result === 'string')
143
- return result;
144
- try {
145
- return JSON.stringify(result);
146
- }
147
- catch {
148
- return String(result);
149
- }
150
- }
151
- truncate(text, maxLen) {
152
- return text.length > maxLen ? text.substring(0, maxLen) + '...' : text;
153
- }
154
- formatTaskStats(event) {
155
- const parts = [];
156
- if (event.toolUses)
157
- parts.push(`${event.toolUses} tools`);
158
- if (event.durationMs)
159
- parts.push(`${Math.round(event.durationMs / 1000)}s`);
160
- return parts.join(', ');
161
- }
162
- }
@@ -1,87 +0,0 @@
1
- /**
2
- * Reload Hooks
3
- *
4
- * Extracted from index.ts main() for testability. Builds the ReloadHooks
5
- * implementation used by AgentRegistry.reload() to drain/disconnect/start
6
- * channels during a hot reload.
7
- */
8
- import { logger } from '../utils/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,122 +0,0 @@
1
- import fs from 'fs';
2
- import path from 'path';
3
- import { getPackageRoot, resolveRoot } from '../paths.js';
4
- import { logger } from '../utils/logger.js';
5
- const KNOWN_SECTIONS = new Set(['runtime', 'group', 'proactive']);
6
- const SECTION_RE = /^##\s+(\w+)\s*$/;
7
- let sections = null;
8
- let builtinSections = null;
9
- function parseTemplate(content) {
10
- const result = new Map();
11
- let currentSection = null;
12
- let currentLines = [];
13
- for (const line of content.split('\n')) {
14
- // Stop parsing at horizontal rule separator (documentation follows)
15
- if (/^---\s*$/.test(line)) {
16
- if (currentSection) {
17
- result.set(currentSection, currentLines.join('\n').trim());
18
- }
19
- break;
20
- }
21
- const m = line.match(SECTION_RE);
22
- if (m) {
23
- if (currentSection) {
24
- result.set(currentSection, currentLines.join('\n').trim());
25
- }
26
- const name = m[1];
27
- if (KNOWN_SECTIONS.has(name)) {
28
- currentSection = name;
29
- currentLines = [];
30
- }
31
- else {
32
- currentSection = null;
33
- currentLines = [];
34
- }
35
- }
36
- else if (currentSection) {
37
- currentLines.push(line);
38
- }
39
- }
40
- if (currentSection) {
41
- result.set(currentSection, currentLines.join('\n').trim());
42
- }
43
- return result;
44
- }
45
- function loadBuiltinTemplate() {
46
- const builtinPath = path.join(getPackageRoot(), 'dist', 'templates', 'prompts.md');
47
- const srcPath = path.join(getPackageRoot(), 'src', 'templates', 'prompts.md');
48
- const filePath = fs.existsSync(builtinPath) ? builtinPath : srcPath;
49
- const content = fs.readFileSync(filePath, 'utf-8');
50
- return parseTemplate(content);
51
- }
52
- export function loadPromptTemplates() {
53
- builtinSections = loadBuiltinTemplate();
54
- const userPath = path.join(resolveRoot(), 'data', 'prompts.md');
55
- if (fs.existsSync(userPath)) {
56
- try {
57
- const content = fs.readFileSync(userPath, 'utf-8');
58
- const parsed = parseTemplate(content);
59
- sections = new Map(builtinSections);
60
- for (const [key, value] of parsed) {
61
- sections.set(key, value);
62
- }
63
- logger.info(`[PromptTemplates] Loaded user override: ${userPath}`);
64
- }
65
- catch (err) {
66
- logger.warn(`[PromptTemplates] Failed to load user override (${userPath}), using builtin:`, err);
67
- sections = builtinSections;
68
- }
69
- }
70
- else {
71
- sections = builtinSections;
72
- logger.info(`[PromptTemplates] Using builtin templates`);
73
- }
74
- for (const name of KNOWN_SECTIONS) {
75
- if (!sections.has(name)) {
76
- logger.warn(`[PromptTemplates] Section "${name}" missing, using builtin fallback`);
77
- const fallback = builtinSections.get(name);
78
- if (fallback)
79
- sections.set(name, fallback);
80
- }
81
- }
82
- }
83
- function isTruthy(val) {
84
- if (val === undefined || val === null || val === false || val === '' || val === 0)
85
- return false;
86
- return true;
87
- }
88
- function renderTemplate(template, vars) {
89
- // Pass 1: conditional sections {{?key}}...{{/}}
90
- let result = template.replace(/\{\{\?(\w+)\}\}([\s\S]*?)\{\{\/\}\}/g, (_match, key, body) => {
91
- return isTruthy(vars[key]) ? body : '';
92
- });
93
- // Pass 2: variable substitution {{key}}
94
- result = result.replace(/\{\{(\w+)\}\}/g, (_match, key) => {
95
- const val = vars[key];
96
- if (!isTruthy(val))
97
- return '';
98
- return String(val);
99
- });
100
- // Pass 3: remove blank lines
101
- return result.split('\n').filter(line => line.trim() !== '').join('\n');
102
- }
103
- export function renderPromptSection(section, vars) {
104
- if (!sections)
105
- loadPromptTemplates();
106
- const template = sections.get(section);
107
- if (!template) {
108
- logger.warn(`[PromptTemplates] Section "${section}" not found`);
109
- return '';
110
- }
111
- return renderTemplate(template, vars);
112
- }
113
- /** Reset loaded templates (for testing) */
114
- export function _resetTemplates() {
115
- sections = null;
116
- builtinSections = null;
117
- }
118
- /** Load templates from a raw string (for testing) */
119
- export function _loadFromString(content) {
120
- builtinSections = parseTemplate(content);
121
- sections = builtinSections;
122
- }
@@ -1,66 +0,0 @@
1
- ---
2
- name: evolclaw-ctl
3
- version: 1.1.0
4
- description: 仅在 evolclaw 运行时可用
5
- trigger: 用户询问或需要切换模型、调整推理强度、查看运行状态、压缩上下文、检查通道健康、管理权限模式、重启服务、重连渠道等
6
- ---
7
-
8
- # EvolClaw Ctl
9
-
10
- 通过 `evolclaw ctl <command> [args]` 管理运行时配置。仅在 evolclaw 托管环境中可用(`EVOLCLAW_SESSION_ID` 已设置)。
11
-
12
- ## 可用指令
13
-
14
- ### 查询类(所有用户)
15
- - `evolclaw ctl help` — 显示帮助
16
- - `evolclaw ctl status` — 显示会话状态
17
- - `evolclaw ctl check` — 检查渠道健康状态
18
-
19
- ### 配置类(管理员)
20
- - `evolclaw ctl model` — 查看当前模型和可选列表
21
- - `evolclaw ctl model <model-id>` — 切换模型(如 `opus`, `sonnet`, `haiku`)
22
- - `evolclaw ctl effort` — 查看当前推理强度
23
- - `evolclaw ctl effort <low|medium|high|max>` — 切换推理强度
24
- - `evolclaw ctl compact` — 压缩当前会话上下文
25
-
26
- ### 权限类
27
- - `evolclaw ctl perm` — 查看当前权限模式(管理员)
28
- - `evolclaw ctl perm <mode>` — 切换权限模式(仅 owner)
29
-
30
- ### 运维类(仅 owner)
31
- - `evolclaw ctl activity <all|dm|owner|none>` — 查看/控制中间输出显示模式
32
- - `evolclaw ctl send [channel] <path>` — 发送项目内文件(仅限项目目录内)
33
- - `evolclaw ctl restart` — 重启服务(慎用:中断所有会话)
34
- - `evolclaw ctl restart <channel>` — 重连指定渠道(管理员可用)
35
- - `evolclaw ctl agentmd` — 查看当前 agent.md
36
- - `evolclaw ctl agentmd put` — 发布本地 agent.md
37
- - `evolclaw ctl agentmd set <内容>` — 直接设置 agent.md 内容
38
- - `evolclaw ctl aid` — 列出所有 AUN 实例及连接状态
39
- - `evolclaw ctl aid new <aid>` — 创建新 AID 并热加载(仅 AUN 通道)
40
-
41
- ## 使用示例
42
-
43
- ```bash
44
- # 查看当前模型
45
- evolclaw ctl model
46
-
47
- # 切换到 opus
48
- evolclaw ctl model opus
49
-
50
- # 降低推理强度以加快响应
51
- evolclaw ctl effort low
52
-
53
- # 压缩上下文
54
- evolclaw ctl compact
55
-
56
- # 查看服务状态
57
- evolclaw ctl status
58
- ```
59
-
60
- ## 注意事项
61
-
62
- - 仅在 evolclaw 托管环境中可用(EVOLCLAW_SESSION_ID 环境变量已设置时)
63
- - 权限继承当前会话用户的角色(owner / admin / guest)
64
- - `compact` 不能在当前会话处理消息期间执行
65
- - `send` 只能发送项目目录下的文件
66
- - `restart` 会中断当前所有会话,谨慎使用