evolclaw 3.0.0 → 3.1.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.
- package/README.md +1 -1
- package/bin/ec.js +29 -0
- package/dist/agents/baseagent-normalize.js +19 -0
- package/dist/agents/claude-runner.js +7 -9
- package/dist/agents/codex-runner.js +2 -0
- package/dist/agents/gemini-runner.js +9 -9
- package/dist/agents/kit-renderer.js +281 -0
- package/dist/aun/aid/identity.js +28 -0
- package/dist/aun/aid/index.js +1 -1
- package/dist/aun/aid/lifecycle-log.js +33 -0
- package/dist/aun/msg/group.js +3 -1
- package/dist/aun/msg/p2p.js +4 -1
- package/dist/channels/aun.js +353 -125
- package/dist/channels/dingtalk.js +2 -1
- package/dist/channels/feishu.js +118 -5
- package/dist/channels/qqbot.js +2 -1
- package/dist/channels/wechat.js +3 -1
- package/dist/channels/wecom.js +2 -1
- package/dist/cli/bench.js +1219 -0
- package/dist/cli/index.js +279 -19
- package/dist/cli/link-rules.js +245 -0
- package/dist/cli/net-check.js +640 -0
- package/dist/cli/watch-msg.js +589 -0
- package/dist/config-store.js +37 -5
- package/dist/core/channel-loader.js +23 -10
- package/dist/core/command-handler.js +46 -22
- package/dist/core/evolagent.js +5 -10
- package/dist/core/message/im-renderer.js +50 -44
- package/dist/core/message/items-formatter.js +11 -4
- package/dist/core/message/message-bridge.js +7 -2
- package/dist/core/message/message-log.js +2 -0
- package/dist/core/message/message-processor.js +150 -99
- package/dist/core/message/message-queue.js +10 -3
- package/dist/core/permission.js +95 -3
- package/dist/core/session/session-manager.js +98 -64
- package/dist/core/trigger/scheduler.js +1 -1
- package/dist/data/error-dict.json +118 -0
- package/dist/eck/baseagent-caps.js +18 -0
- package/dist/eck/detect.js +47 -0
- package/dist/eck/init.js +77 -0
- package/dist/eck/rules-loader.js +28 -0
- package/dist/index.js +137 -16
- package/dist/net-check.js +640 -0
- package/dist/paths.js +31 -40
- package/dist/utils/aid-lifecycle-log.js +33 -0
- package/dist/utils/atomic-write.js +10 -0
- package/dist/utils/cross-platform.js +17 -8
- package/dist/utils/error-utils.js +10 -2
- package/dist/utils/instance-registry.js +6 -5
- package/dist/utils/log-writer.js +2 -1
- package/dist/utils/logger.js +10 -0
- package/dist/utils/npm-ops.js +35 -3
- package/dist/utils/process-introspect.js +16 -38
- package/dist/watch-msg.js +26 -11
- package/evolclaw-install-aun.md +14 -2
- package/kits/docs/GUIDE.md +20 -0
- package/kits/docs/INDEX.md +52 -0
- package/kits/docs/aun/CHEATSHEET.md +17 -0
- package/kits/docs/aun/SYNC_PROTOCOL.md +15 -0
- package/kits/docs/channels/feishu.md +27 -0
- package/kits/docs/eck_templates/GUIDE.template.md +22 -0
- package/kits/docs/eck_templates/INDEX.template.md +28 -0
- package/kits/docs/eck_templates/path-registry.template.md +33 -0
- package/kits/docs/eck_templates/runtime.template.md +19 -0
- package/kits/docs/evolclaw/MSG_GROUP.md +30 -0
- package/kits/docs/evolclaw/MSG_PRIVATE.md +25 -0
- package/kits/docs/identity/AID_PROFILE_SPEC.md +27 -0
- package/kits/docs/identity/PATH_OPS.md +16 -0
- package/kits/docs/identity/ROLE_DETAIL.md +20 -0
- package/kits/docs/path-registry.md +43 -0
- package/kits/eck_manifest.json +95 -0
- package/kits/rules/01-overview.md +120 -0
- package/kits/rules/02-navigation.md +75 -0
- package/kits/rules/03-identity.md +34 -0
- package/kits/rules/04-relation.md +49 -0
- package/kits/rules/05-venue.md +45 -0
- package/kits/rules/06-channel.md +43 -0
- package/kits/templates/system-fragments/baseagent.md +2 -0
- package/kits/templates/system-fragments/channel.md +10 -0
- package/kits/templates/system-fragments/identity.md +12 -0
- package/kits/templates/system-fragments/relation.md +9 -0
- package/kits/templates/system-fragments/runtime.md +19 -0
- package/kits/templates/system-fragments/venue.md +5 -0
- package/package.json +7 -5
- package/dist/agents/templates.js +0 -122
- package/dist/data/prompts.md +0 -137
- package/kits/aun/meta.md +0 -25
- package/kits/aun/role.md +0 -25
- package/kits/templates/group.md +0 -20
- package/kits/templates/private.md +0 -9
- package/kits/templates/system-fragments/personal-context.md +0 -3
- package/kits/templates/system-fragments/self-intro.md +0 -5
- package/kits/templates/system-fragments/speaker-intro.md +0 -5
- package/kits/templates/system-fragments/venue-intro.md +0 -5
- /package/kits/{channels → docs/channels}/aun.md +0 -0
- /package/kits/{evolclaw/commands.md → docs/evolclaw/AGENT_CMD.md} +0 -0
- /package/kits/{evolclaw → docs/evolclaw}/self-summary.md +0 -0
- /package/kits/{evolclaw → docs/evolclaw}/tools.md +0 -0
- /package/kits/{evolclaw → docs/identity}/identity-tools.md +0 -0
package/README.md
CHANGED
|
@@ -263,7 +263,7 @@ evolclaw/
|
|
|
263
263
|
- `/check` - 系统健康检查(详情)
|
|
264
264
|
- `/activity [all|dm|owner|none]` - 查看/控制中间输出显示模式
|
|
265
265
|
- `/chatmode [interactive|proactive]` - 查看/切换会话模式
|
|
266
|
-
- `/dispatch [mention|
|
|
266
|
+
- `/dispatch [mention|broadcast]` - 群聊分发模式(仅 @ 响应或广播)
|
|
267
267
|
- `/trigger <动作> ...` - 设置/查看 AI 自主触发器(延迟/定时/周期)
|
|
268
268
|
- `/restart <channel>` - 重连指定渠道
|
|
269
269
|
|
package/bin/ec.js
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import fs from 'fs';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import { spawnSync } from 'child_process';
|
|
5
|
+
import { fileURLToPath, pathToFileURL } from 'url';
|
|
6
|
+
import { createRequire } from 'module';
|
|
7
|
+
|
|
8
|
+
const require = createRequire(import.meta.url);
|
|
9
|
+
const here = path.dirname(fileURLToPath(import.meta.url));
|
|
10
|
+
const repoRoot = path.resolve(here, '..');
|
|
11
|
+
const srcEntry = path.join(repoRoot, 'src', 'cli', 'index.ts');
|
|
12
|
+
const distEntry = path.join(repoRoot, 'dist', 'cli', 'index.js');
|
|
13
|
+
const args = process.argv.slice(2);
|
|
14
|
+
|
|
15
|
+
if (fs.existsSync(srcEntry)) {
|
|
16
|
+
try {
|
|
17
|
+
const tsxImport = pathToFileURL(require.resolve('tsx')).href;
|
|
18
|
+
const result = spawnSync(process.execPath, ['--import', tsxImport, srcEntry, ...args], { stdio: 'inherit' });
|
|
19
|
+
process.exit(result.status ?? (result.error ? 1 : 0));
|
|
20
|
+
} catch {}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
if (fs.existsSync(distEntry)) {
|
|
24
|
+
const result = spawnSync(process.execPath, [distEntry, ...args], { stdio: 'inherit' });
|
|
25
|
+
process.exit(result.status ?? (result.error ? 1 : 0));
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
console.error('ec: missing CLI entrypoint');
|
|
29
|
+
process.exit(1);
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
const BASEAGENT_ALIASES = {
|
|
2
|
+
claude: { canonical: 'claude', displayName: 'Claude Code' },
|
|
3
|
+
cc: { canonical: 'claude', displayName: 'Claude Code' },
|
|
4
|
+
'claude-code': { canonical: 'claude', displayName: 'Claude Code' },
|
|
5
|
+
'claude code': { canonical: 'claude', displayName: 'Claude Code' },
|
|
6
|
+
claudecode: { canonical: 'claude', displayName: 'Claude Code' },
|
|
7
|
+
codex: { canonical: 'codex', displayName: 'Codex' },
|
|
8
|
+
'codex-cli': { canonical: 'codex', displayName: 'Codex' },
|
|
9
|
+
'codex cli': { canonical: 'codex', displayName: 'Codex' },
|
|
10
|
+
gemini: { canonical: 'gemini', displayName: 'Gemini CLI' },
|
|
11
|
+
'gemini-cli': { canonical: 'gemini', displayName: 'Gemini CLI' },
|
|
12
|
+
'gemini cli': { canonical: 'gemini', displayName: 'Gemini CLI' },
|
|
13
|
+
geminicli: { canonical: 'gemini', displayName: 'Gemini CLI' },
|
|
14
|
+
hermes: { canonical: 'hermes', displayName: 'Hermes' },
|
|
15
|
+
};
|
|
16
|
+
export function normalizeBaseagent(input) {
|
|
17
|
+
const key = String(input || '').trim().toLowerCase().replace(/_/g, '-');
|
|
18
|
+
return BASEAGENT_ALIASES[key] || { canonical: 'unknown', displayName: input ? String(input) : 'Unknown' };
|
|
19
|
+
}
|
|
@@ -494,7 +494,6 @@ export class AgentRunner {
|
|
|
494
494
|
* 所有 SDK 特有的事件类型引用封装在此方法内
|
|
495
495
|
*/
|
|
496
496
|
async *transformStream(sdkStream, sessionId) {
|
|
497
|
-
let hasTextDelta = false;
|
|
498
497
|
let lastSessionId;
|
|
499
498
|
// tool_use_id → tool_name 映射,用于从 SDKUserMessage 的 tool_result 块中还原工具名
|
|
500
499
|
const toolUseNames = new Map();
|
|
@@ -505,11 +504,6 @@ export class AgentRunner {
|
|
|
505
504
|
this.updateSessionId(sessionId, event.session_id);
|
|
506
505
|
yield { type: 'session_id', sessionId: event.session_id };
|
|
507
506
|
}
|
|
508
|
-
// text_delta → text
|
|
509
|
-
if (event.type === 'text_delta' && event.text) {
|
|
510
|
-
hasTextDelta = true;
|
|
511
|
-
yield { type: 'text', text: event.text };
|
|
512
|
-
}
|
|
513
507
|
// system: compact_boundary → compact
|
|
514
508
|
if (event.type === 'system' && event.subtype === 'compact_boundary') {
|
|
515
509
|
yield { type: 'compact', preTokens: event.compact_metadata?.pre_tokens || 0 };
|
|
@@ -536,7 +530,7 @@ export class AgentRunner {
|
|
|
536
530
|
toolUseNames.set(content.id, content.name);
|
|
537
531
|
yield { type: 'tool_use', name: content.name, input: content.input, callId: content.id };
|
|
538
532
|
}
|
|
539
|
-
else if (content.type === 'text' && content.text
|
|
533
|
+
else if (content.type === 'text' && content.text) {
|
|
540
534
|
yield { type: 'text', text: content.text };
|
|
541
535
|
}
|
|
542
536
|
}
|
|
@@ -589,7 +583,11 @@ export class AgentRunner {
|
|
|
589
583
|
costUsd: event.total_cost_usd,
|
|
590
584
|
terminalReason: event.terminal_reason,
|
|
591
585
|
sessionTitle: event.session_title,
|
|
586
|
+
numTurns: event.num_turns,
|
|
587
|
+
usage: event.usage,
|
|
592
588
|
};
|
|
589
|
+
// result 是 SDK 流的终结事件,不再等待后续(防止 interrupt 后流不关闭导致挂起)
|
|
590
|
+
return;
|
|
593
591
|
}
|
|
594
592
|
}
|
|
595
593
|
}
|
|
@@ -762,7 +760,7 @@ export class AgentRunner {
|
|
|
762
760
|
const sdkPermissionMode = this.toSdkPermissionMode();
|
|
763
761
|
logger.info(`[AgentRunner] runQuery model=${this.model} effort=${this.effort ?? 'auto'} permMode=${this.permissionMode} sdkMode=${sdkPermissionMode}`);
|
|
764
762
|
if (systemPromptAppend) {
|
|
765
|
-
logger.info(`[AgentRunner] systemPromptAppend
|
|
763
|
+
logger.info(`[AgentRunner] systemPromptAppend: ${systemPromptAppend.length} chars`);
|
|
766
764
|
}
|
|
767
765
|
else {
|
|
768
766
|
logger.info(`[AgentRunner] systemPromptAppend: none`);
|
|
@@ -1059,7 +1057,7 @@ export class AgentRunner {
|
|
|
1059
1057
|
export class ClaudeAgentPlugin {
|
|
1060
1058
|
name = 'claude';
|
|
1061
1059
|
isEnabled(agent) {
|
|
1062
|
-
return
|
|
1060
|
+
return agent.baseagent === 'claude';
|
|
1063
1061
|
}
|
|
1064
1062
|
createAgent(agent, callbacks) {
|
|
1065
1063
|
const override = agent.config.baseagents?.claude;
|
|
@@ -14,6 +14,7 @@ import fs from 'fs';
|
|
|
14
14
|
import path from 'path';
|
|
15
15
|
import os from 'os';
|
|
16
16
|
import { resolveGoogleConfig } from './resolve.js';
|
|
17
|
+
import { commandExists } from '../utils/cross-platform.js';
|
|
17
18
|
import { GeminiSessionFileAdapter } from '../core/session/adapters/gemini-session-file-adapter.js';
|
|
18
19
|
import { logger } from '../utils/logger.js';
|
|
19
20
|
// Strip ANSI escape codes from Gemini CLI text output.
|
|
@@ -406,17 +407,16 @@ export class GeminiRunner {
|
|
|
406
407
|
export class GeminiAgentPlugin {
|
|
407
408
|
name = 'gemini';
|
|
408
409
|
isEnabled(agent) {
|
|
409
|
-
if (
|
|
410
|
+
if (agent.baseagent !== 'gemini')
|
|
410
411
|
return false;
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
const syntheticConfig = { agents: { gemini: override } };
|
|
414
|
-
const resolved = resolveGoogleConfig(syntheticConfig, override);
|
|
415
|
-
return !!resolved.cliPath;
|
|
416
|
-
}
|
|
417
|
-
catch {
|
|
412
|
+
const geminiCfg = agent.config.baseagents?.gemini;
|
|
413
|
+
if (!geminiCfg)
|
|
418
414
|
return false;
|
|
419
|
-
|
|
415
|
+
if (geminiCfg.cliPath)
|
|
416
|
+
return true;
|
|
417
|
+
if (geminiCfg.apiKey && !geminiCfg.apiKey.includes('your-') && !geminiCfg.apiKey.includes('placeholder'))
|
|
418
|
+
return true;
|
|
419
|
+
return commandExists('gemini');
|
|
420
420
|
}
|
|
421
421
|
createAgent(agent, callbacks) {
|
|
422
422
|
const override = agent.config.baseagents?.gemini;
|
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { kitsDir, eckDebugDir, resolveRoot } from '../paths.js';
|
|
4
|
+
import { logger } from '../utils/logger.js';
|
|
5
|
+
// ── Param descriptions (for debug output) ──
|
|
6
|
+
const PARAM_DESCRIPTIONS = {
|
|
7
|
+
EVOLCLAW_HOME: '用户数据根目录',
|
|
8
|
+
PACKAGE_ROOT: 'evolclaw 包根目录',
|
|
9
|
+
CURRENT_PROJECT: '当前项目完整路径',
|
|
10
|
+
selfAid: '当前 agent 的 AID',
|
|
11
|
+
selfName: '当前 agent 的显示名',
|
|
12
|
+
hasPersona: '是否有 persona 内容',
|
|
13
|
+
hasWorkingMemory: '是否有 working memory',
|
|
14
|
+
peerId: '对端在该渠道的原生 ID',
|
|
15
|
+
peerKey: '对端跨渠道唯一标识(channel#urlEncode(peerId))',
|
|
16
|
+
peerName: '对端显示名',
|
|
17
|
+
peerRole: '对端角色',
|
|
18
|
+
groupId: '群组 ID(群聊时)',
|
|
19
|
+
scene: '场景类型',
|
|
20
|
+
chatType: '聊天类型',
|
|
21
|
+
channel: '当前渠道',
|
|
22
|
+
venueUid: 'venue 唯一标识',
|
|
23
|
+
project: '当前项目目录名(由 CURRENT_PROJECT 派生)',
|
|
24
|
+
sessionName: '会话名称',
|
|
25
|
+
sessionMode: '会话模式',
|
|
26
|
+
readonly: '是否只读模式',
|
|
27
|
+
canSendFile: '当前渠道是否支持发文件',
|
|
28
|
+
capabilities: '渠道能力列表',
|
|
29
|
+
baseAgent: '当前 base agent 规范值(claude/codex/gemini/hermes)',
|
|
30
|
+
baseAgentName: '当前 base agent 显示名',
|
|
31
|
+
};
|
|
32
|
+
// ── Cache ──
|
|
33
|
+
let _manifestCache = null;
|
|
34
|
+
const _sessionPathCache = new Map();
|
|
35
|
+
// ── Public API ──
|
|
36
|
+
export function loadKitManifest() {
|
|
37
|
+
_manifestCache = loadAndMergeManifest();
|
|
38
|
+
logger.info(`[KitRenderer] Loaded manifest: ${_manifestCache.length} sections`);
|
|
39
|
+
}
|
|
40
|
+
export function invalidateKitCache() {
|
|
41
|
+
_manifestCache = null;
|
|
42
|
+
_sessionPathCache.clear();
|
|
43
|
+
}
|
|
44
|
+
export function invalidateSessionCache(sessionId) {
|
|
45
|
+
_sessionPathCache.delete(sessionId);
|
|
46
|
+
}
|
|
47
|
+
export function renderKitSections(ctx) {
|
|
48
|
+
if (!_manifestCache)
|
|
49
|
+
loadKitManifest();
|
|
50
|
+
const sections = _manifestCache;
|
|
51
|
+
const fileParts = [];
|
|
52
|
+
for (const section of sections) {
|
|
53
|
+
if (section.enabled === false)
|
|
54
|
+
continue;
|
|
55
|
+
if (!evaluateWhen(section.when, ctx.vars))
|
|
56
|
+
continue;
|
|
57
|
+
const files = loadSectionFiles(section, ctx);
|
|
58
|
+
if (files.length === 0)
|
|
59
|
+
continue;
|
|
60
|
+
for (const [filePath, rawContent] of files) {
|
|
61
|
+
const content = section.needsInjection ? renderTemplate(rawContent, ctx.vars) : rawContent;
|
|
62
|
+
if (!content.trim())
|
|
63
|
+
continue;
|
|
64
|
+
const label = section.description ? `${section.id} — ${section.description}` : section.id;
|
|
65
|
+
fileParts.push(`Contenu de ${filePath} (${label}):\n\n${content.trimEnd()}`);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
if (fileParts.length === 0)
|
|
69
|
+
return '';
|
|
70
|
+
const body = fileParts.join('\n\n');
|
|
71
|
+
const output = `<system-reminder>\nEvolClaw Context Kit documents are shown below.\n\n${body}\n\nIMPORTANT: Use this context when it affects the current interaction.\n</system-reminder>`;
|
|
72
|
+
writeDebugFiles(ctx, output);
|
|
73
|
+
return output;
|
|
74
|
+
}
|
|
75
|
+
export function cleanEckDebug() {
|
|
76
|
+
const dir = eckDebugDir();
|
|
77
|
+
const cutoff = Date.now() - 24 * 60 * 60 * 1000;
|
|
78
|
+
try {
|
|
79
|
+
for (const f of fs.readdirSync(dir)) {
|
|
80
|
+
const fp = path.join(dir, f);
|
|
81
|
+
try {
|
|
82
|
+
if (fs.statSync(fp).mtimeMs < cutoff)
|
|
83
|
+
fs.unlinkSync(fp);
|
|
84
|
+
}
|
|
85
|
+
catch { /* skip */ }
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
catch { /* dir doesn't exist yet */ }
|
|
89
|
+
}
|
|
90
|
+
// CHUNK_CONTINUE_2
|
|
91
|
+
// ── Manifest loading ──
|
|
92
|
+
function loadAndMergeManifest() {
|
|
93
|
+
const kitsPath = path.join(kitsDir(), 'eck_manifest.json');
|
|
94
|
+
const eckPath = path.join(resolveRoot(), 'eck', 'eck_manifest.json');
|
|
95
|
+
let base;
|
|
96
|
+
try {
|
|
97
|
+
base = JSON.parse(fs.readFileSync(kitsPath, 'utf-8'));
|
|
98
|
+
}
|
|
99
|
+
catch (err) {
|
|
100
|
+
logger.error(`[KitRenderer] Failed to load kits/eck_manifest.json: ${err}`);
|
|
101
|
+
return [];
|
|
102
|
+
}
|
|
103
|
+
if (!fs.existsSync(eckPath)) {
|
|
104
|
+
return sortSections(base.sections);
|
|
105
|
+
}
|
|
106
|
+
try {
|
|
107
|
+
const override = JSON.parse(fs.readFileSync(eckPath, 'utf-8'));
|
|
108
|
+
if (override.mode === 'replace') {
|
|
109
|
+
return sortSections(override.sections);
|
|
110
|
+
}
|
|
111
|
+
const merged = new Map();
|
|
112
|
+
for (const s of base.sections)
|
|
113
|
+
merged.set(s.id, { ...s });
|
|
114
|
+
for (const s of override.sections) {
|
|
115
|
+
const existing = merged.get(s.id);
|
|
116
|
+
if (existing) {
|
|
117
|
+
merged.set(s.id, { ...existing, ...s });
|
|
118
|
+
}
|
|
119
|
+
else {
|
|
120
|
+
merged.set(s.id, s);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
return sortSections([...merged.values()]);
|
|
124
|
+
}
|
|
125
|
+
catch (err) {
|
|
126
|
+
logger.warn(`[KitRenderer] Failed to load eck override, using kits only: ${err}`);
|
|
127
|
+
return sortSections(base.sections);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
function sortSections(sections) {
|
|
131
|
+
return sections.slice().sort((a, b) => a.order - b.order);
|
|
132
|
+
}
|
|
133
|
+
// ── Section content loading ──
|
|
134
|
+
function loadSectionFiles(section, ctx) {
|
|
135
|
+
if (section.type === 'file' && section.file) {
|
|
136
|
+
const result = loadFileSection(section.file, ctx);
|
|
137
|
+
return result ? [result] : [];
|
|
138
|
+
}
|
|
139
|
+
if (section.type === 'directory' && section.path) {
|
|
140
|
+
return loadDirectorySection(section.path, section.pattern, ctx);
|
|
141
|
+
}
|
|
142
|
+
return [];
|
|
143
|
+
}
|
|
144
|
+
function loadFileSection(filePath, ctx) {
|
|
145
|
+
const resolved = resolvePath(filePath, ctx);
|
|
146
|
+
if (!resolved)
|
|
147
|
+
return null;
|
|
148
|
+
const sessionCache = getSessionCache(ctx.sessionId);
|
|
149
|
+
if (sessionCache.has(resolved))
|
|
150
|
+
return [resolved, sessionCache.get(resolved)];
|
|
151
|
+
try {
|
|
152
|
+
const content = fs.readFileSync(resolved, 'utf-8');
|
|
153
|
+
sessionCache.set(resolved, content);
|
|
154
|
+
return [resolved, content];
|
|
155
|
+
}
|
|
156
|
+
catch {
|
|
157
|
+
return null;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
function loadDirectorySection(dirPath, pattern, ctx) {
|
|
161
|
+
const resolved = resolvePath(dirPath, ctx);
|
|
162
|
+
if (!resolved)
|
|
163
|
+
return [];
|
|
164
|
+
return readDirectoryFiles(resolved, pattern).map(([name, content]) => [path.join(resolved, name), content]);
|
|
165
|
+
}
|
|
166
|
+
// ── Path resolution ──
|
|
167
|
+
function resolvePath(rawPath, ctx) {
|
|
168
|
+
let resolved = rawPath.replace(/\$([A-Z_]+)/g, (_, name) => {
|
|
169
|
+
const val = ctx.vars[name];
|
|
170
|
+
if (val === undefined || val === null || val === false || val === '')
|
|
171
|
+
return '';
|
|
172
|
+
return String(val);
|
|
173
|
+
});
|
|
174
|
+
resolved = resolved.replace(/\{\{(\w+)\}\}/g, (_, key) => {
|
|
175
|
+
const val = ctx.vars[key];
|
|
176
|
+
if (val === undefined || val === null || val === false || val === '')
|
|
177
|
+
return '';
|
|
178
|
+
return String(val);
|
|
179
|
+
});
|
|
180
|
+
if (!resolved || resolved.includes('$') || resolved.includes('{{'))
|
|
181
|
+
return null;
|
|
182
|
+
if (!fs.existsSync(resolved))
|
|
183
|
+
return null;
|
|
184
|
+
return resolved;
|
|
185
|
+
}
|
|
186
|
+
// CHUNK_CONTINUE_5
|
|
187
|
+
// ── Directory reading ──
|
|
188
|
+
function readDirectoryFiles(dirPath, pattern) {
|
|
189
|
+
const glob = pattern || '*.md';
|
|
190
|
+
try {
|
|
191
|
+
const files = fs.readdirSync(dirPath)
|
|
192
|
+
.filter(f => matchGlob(f, glob))
|
|
193
|
+
.sort();
|
|
194
|
+
return files.map(f => {
|
|
195
|
+
const content = fs.readFileSync(path.join(dirPath, f), 'utf-8');
|
|
196
|
+
return [f, content];
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
catch {
|
|
200
|
+
return [];
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
function matchGlob(filename, pattern) {
|
|
204
|
+
const regex = pattern
|
|
205
|
+
.replace(/\./g, '\\.')
|
|
206
|
+
.replace(/\*/g, '.*')
|
|
207
|
+
.replace(/\{([^}]+)\}/g, (_, alts) => `(${alts.split(',').join('|')})`);
|
|
208
|
+
return new RegExp(`^${regex}$`).test(filename);
|
|
209
|
+
}
|
|
210
|
+
// ── When condition evaluation ──
|
|
211
|
+
function evaluateWhen(when, vars) {
|
|
212
|
+
if (when === 'always')
|
|
213
|
+
return true;
|
|
214
|
+
if (when.var !== undefined) {
|
|
215
|
+
const val = vars[when.var];
|
|
216
|
+
if (when.eq !== undefined)
|
|
217
|
+
return val === when.eq;
|
|
218
|
+
if (when.neq !== undefined)
|
|
219
|
+
return val !== when.neq;
|
|
220
|
+
if (when.in !== undefined)
|
|
221
|
+
return when.in.includes(val);
|
|
222
|
+
if (when.nin !== undefined)
|
|
223
|
+
return !when.nin.includes(val);
|
|
224
|
+
}
|
|
225
|
+
if (when.any)
|
|
226
|
+
return when.any.some(k => isTruthy(vars[k]));
|
|
227
|
+
if (when.all)
|
|
228
|
+
return when.all.every(k => isTruthy(vars[k]));
|
|
229
|
+
return true;
|
|
230
|
+
}
|
|
231
|
+
function isTruthy(val) {
|
|
232
|
+
return val !== undefined && val !== null && val !== false && val !== '' && val !== 0;
|
|
233
|
+
}
|
|
234
|
+
// CHUNK_CONTINUE_6
|
|
235
|
+
// ── Template rendering ──
|
|
236
|
+
function renderTemplate(template, vars) {
|
|
237
|
+
// Pass 1: conditional sections {{?key=value}}...{{/}} and {{?key}}...{{/}}
|
|
238
|
+
let result = template.replace(/\{\{\?(\w+)(?:=([^}]*))?\}\}([\s\S]*?)\{\{\/\}\}/g, (_match, key, value, body) => {
|
|
239
|
+
if (value !== undefined) {
|
|
240
|
+
return String(vars[key]) === value ? body : '';
|
|
241
|
+
}
|
|
242
|
+
return isTruthy(vars[key]) ? body : '';
|
|
243
|
+
});
|
|
244
|
+
// Pass 2: variable substitution {{key}}
|
|
245
|
+
result = result.replace(/\{\{(\w+)\}\}/g, (_match, key) => {
|
|
246
|
+
const val = vars[key];
|
|
247
|
+
if (!isTruthy(val))
|
|
248
|
+
return '';
|
|
249
|
+
return String(val);
|
|
250
|
+
});
|
|
251
|
+
// Pass 3: remove blank lines
|
|
252
|
+
return result.split('\n').filter(line => line.trim() !== '').join('\n');
|
|
253
|
+
}
|
|
254
|
+
// ── Session cache helper ──
|
|
255
|
+
function getSessionCache(sessionId) {
|
|
256
|
+
let cache = _sessionPathCache.get(sessionId);
|
|
257
|
+
if (!cache) {
|
|
258
|
+
cache = new Map();
|
|
259
|
+
_sessionPathCache.set(sessionId, cache);
|
|
260
|
+
}
|
|
261
|
+
return cache;
|
|
262
|
+
}
|
|
263
|
+
// ── Debug output ──
|
|
264
|
+
function writeDebugFiles(ctx, output) {
|
|
265
|
+
const now = new Date();
|
|
266
|
+
const ts = now.toISOString().replace(/[T:.]/g, '-').slice(0, 19);
|
|
267
|
+
const dir = eckDebugDir();
|
|
268
|
+
const varsData = {
|
|
269
|
+
timestamp: now.toISOString(),
|
|
270
|
+
sessionId: ctx.sessionId,
|
|
271
|
+
params: Object.entries(ctx.vars)
|
|
272
|
+
.filter(([, v]) => v !== undefined && v !== null)
|
|
273
|
+
.map(([name, value]) => ({
|
|
274
|
+
name,
|
|
275
|
+
value,
|
|
276
|
+
description: PARAM_DESCRIPTIONS[name] || '',
|
|
277
|
+
})),
|
|
278
|
+
};
|
|
279
|
+
fs.writeFile(path.join(dir, `vars-${ts}.json`), JSON.stringify(varsData, null, 2), () => { });
|
|
280
|
+
fs.writeFile(path.join(dir, `context-${ts}.md`), output, () => { });
|
|
281
|
+
}
|
package/dist/aun/aid/identity.js
CHANGED
|
@@ -3,6 +3,7 @@ import path from 'path';
|
|
|
3
3
|
import os from 'os';
|
|
4
4
|
import crypto from 'crypto';
|
|
5
5
|
import { getAunClient, downloadCaRoot } from './client.js';
|
|
6
|
+
import { resolvePaths } from '../../paths.js';
|
|
6
7
|
// ==================== Validation ====================
|
|
7
8
|
export function isValidAid(name) {
|
|
8
9
|
const labels = name.split('.');
|
|
@@ -129,3 +130,30 @@ export async function aidLookup(aid) {
|
|
|
129
130
|
return { exists: false, aid, gateway, error: String(e.message || e) };
|
|
130
131
|
}
|
|
131
132
|
}
|
|
133
|
+
function lifecycleLogPath(aid) {
|
|
134
|
+
const aidName = aid.startsWith('@') ? aid.slice(1) : aid;
|
|
135
|
+
return path.join(resolvePaths().aidLogsDir, `${aidName}.jsonl`);
|
|
136
|
+
}
|
|
137
|
+
export function appendAidLifecycle(event) {
|
|
138
|
+
const filePath = lifecycleLogPath(event.aid);
|
|
139
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
140
|
+
fs.appendFileSync(filePath, JSON.stringify(event) + '\n');
|
|
141
|
+
}
|
|
142
|
+
export function readAidLifecycle(aid, lastN = 50) {
|
|
143
|
+
const filePath = lifecycleLogPath(aid);
|
|
144
|
+
try {
|
|
145
|
+
const content = fs.readFileSync(filePath, 'utf-8');
|
|
146
|
+
const lines = content.trim().split('\n').filter(Boolean);
|
|
147
|
+
const events = [];
|
|
148
|
+
for (const line of lines.slice(-lastN)) {
|
|
149
|
+
try {
|
|
150
|
+
events.push(JSON.parse(line));
|
|
151
|
+
}
|
|
152
|
+
catch { }
|
|
153
|
+
}
|
|
154
|
+
return events;
|
|
155
|
+
}
|
|
156
|
+
catch {
|
|
157
|
+
return [];
|
|
158
|
+
}
|
|
159
|
+
}
|
package/dist/aun/aid/index.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export { isValidAid, aidList, aidCreate, aidShow, aidDelete, aidLookup } from './identity.js';
|
|
1
|
+
export { isValidAid, aidList, aidCreate, aidShow, aidDelete, aidLookup, appendAidLifecycle, readAidLifecycle } from './identity.js';
|
|
2
2
|
export { buildInitialAgentMd, agentmdGet, agentmdPut } from './agentmd.js';
|
|
3
3
|
export { MIN_AUN_CORE_SDK, AUN_CORE_SDK_PKG, isAunSdkVersionOk, resolveAunCoreSdkPkg, ensureAunSdk, isAunSdkReady, downloadCaRoot, getAunClient, suppressSdkLogs, } from './client.js';
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { resolvePaths } from '../../paths.js';
|
|
4
|
+
function ensureDir(dir) {
|
|
5
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
6
|
+
}
|
|
7
|
+
function logPath(aid) {
|
|
8
|
+
const aidName = aid.startsWith('@') ? aid.slice(1) : aid;
|
|
9
|
+
return path.join(resolvePaths().aidLogsDir, `${aidName}.jsonl`);
|
|
10
|
+
}
|
|
11
|
+
export function appendAidLifecycle(event) {
|
|
12
|
+
const filePath = logPath(event.aid);
|
|
13
|
+
ensureDir(path.dirname(filePath));
|
|
14
|
+
fs.appendFileSync(filePath, JSON.stringify(event) + '\n');
|
|
15
|
+
}
|
|
16
|
+
export function readAidLifecycle(aid, lastN = 50) {
|
|
17
|
+
const filePath = logPath(aid);
|
|
18
|
+
try {
|
|
19
|
+
const content = fs.readFileSync(filePath, 'utf-8');
|
|
20
|
+
const lines = content.trim().split('\n').filter(Boolean);
|
|
21
|
+
const events = [];
|
|
22
|
+
for (const line of lines.slice(-lastN)) {
|
|
23
|
+
try {
|
|
24
|
+
events.push(JSON.parse(line));
|
|
25
|
+
}
|
|
26
|
+
catch { }
|
|
27
|
+
}
|
|
28
|
+
return events;
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
return [];
|
|
32
|
+
}
|
|
33
|
+
}
|
package/dist/aun/msg/group.js
CHANGED
|
@@ -25,7 +25,9 @@ export async function groupSend(args) {
|
|
|
25
25
|
if (args.mentions && args.mentions.length > 0) {
|
|
26
26
|
payload.mentions = args.mentions;
|
|
27
27
|
}
|
|
28
|
-
const
|
|
28
|
+
const sendParams = { group_id: args.groupId, payload };
|
|
29
|
+
sendParams.encrypt = args.encrypt === true;
|
|
30
|
+
const result = await conn.call('group.send', sendParams);
|
|
29
31
|
return {
|
|
30
32
|
ok: true,
|
|
31
33
|
group_id: result?.group_id ?? args.groupId,
|
package/dist/aun/msg/p2p.js
CHANGED
|
@@ -29,7 +29,10 @@ export async function msgSend(args) {
|
|
|
29
29
|
break;
|
|
30
30
|
}
|
|
31
31
|
}
|
|
32
|
-
const
|
|
32
|
+
const sendParams = { to: args.to, payload };
|
|
33
|
+
// Default: plaintext. Set encrypt: true to enable E2EE.
|
|
34
|
+
sendParams.encrypt = args.encrypt === true;
|
|
35
|
+
const result = await conn.call('message.send', sendParams);
|
|
33
36
|
return {
|
|
34
37
|
ok: true,
|
|
35
38
|
message_id: result?.message_id,
|