evolclaw 3.1.2 → 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.
- package/CHANGELOG.md +21 -0
- package/README.md +2 -6
- package/dist/agents/claude-runner.js +1 -1
- package/dist/agents/codex-runner.js +75 -19
- package/dist/agents/gemini-runner.js +0 -2
- package/dist/agents/kit-renderer.js +59 -10
- package/dist/aun/aid/agentmd.js +49 -27
- package/dist/aun/aid/index.js +1 -1
- package/dist/aun/rpc/connection.js +3 -0
- package/dist/channels/aun.js +67 -14
- package/dist/cli/agent.js +13 -6
- package/dist/cli/index.js +27 -37
- package/dist/cli/init.js +13 -6
- package/dist/core/command-handler.js +615 -534
- package/dist/core/evolagent.js +31 -0
- package/dist/core/message/im-renderer.js +10 -0
- package/dist/core/message/message-bridge.js +123 -24
- package/dist/core/message/message-processor.js +43 -14
- package/dist/core/session/session-manager.js +185 -42
- package/kits/eck_manifest.json +3 -3
- package/kits/rules/02-navigation.md +1 -0
- package/kits/rules/06-channel.md +2 -18
- package/kits/templates/system-fragments/baseagent.md +2 -2
- package/kits/templates/system-fragments/channel.md +18 -9
- package/kits/templates/system-fragments/eckruntime.md +14 -0
- package/kits/templates/system-fragments/identity.md +5 -6
- package/kits/templates/system-fragments/relation.md +7 -5
- package/kits/templates/system-fragments/venue.md +2 -3
- package/package.json +1 -1
- package/kits/templates/system-fragments/runtime.md +0 -19
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,26 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## v3.1.3 (2026-05-26)
|
|
4
|
+
|
|
5
|
+
### New Features
|
|
6
|
+
|
|
7
|
+
- **Menu 协议拆分为 5 动词** — `menu.list` / `menu.query` / `menu.options` / `menu.update` / `menu.action`,response 统一 `error.code` 透传(`NO_ACTIVE_SESSION` / `MISSING_VALUE` / ...),`name → cmd` 由 bridge 内部映射,AUN 白名单接纳全部 `menu.*` 类型
|
|
8
|
+
- **xhigh effort 档位** — Claude/Codex 新增 `xhigh` 推理强度档位,Codex 通过 `codex debug models` 动态拉取模型目录与各模型支持的 effort 列表
|
|
9
|
+
- **结构化 menu options 字段** — session 子菜单返回 `preview`(首条消息预览)/ `lastActive` / `agentSessionId` / `turns` 扩展字段,所有可选项菜单返回 `selected` 标记当前值
|
|
10
|
+
|
|
11
|
+
### Improvements
|
|
12
|
+
|
|
13
|
+
- **命令收敛** — 移除 `/plist` `/project` `/bind` `/agentmd` `/p` 别名;`/agent` 改为 EvolAgent 管理命令;baseagent 切换统一为 `/baseagent`(别名 `/base`);`/aid` `/rpc` `/storage` `/evolagent` 改为 ctl 专属
|
|
14
|
+
- **execMenu 拆分** — `command-handler` 拆出 `execMenuQuery` / `execMenuUpdate` / `execMenuAction` 三入口,无活跃会话时多项 fallback 到 evolagent config(`/pwd` / `/chatmode` / `/dispatch` 等)
|
|
15
|
+
- **EvolAgent 持久化方法** — 新增 `setActiveBaseagent` / `setChatmodePrivate` / `setDispatch`,配置变更直接落盘
|
|
16
|
+
- **Codex 实例每次重建** — 通过 env 注入 `EVOLCLAW_SESSION_ID`,仅首轮拼接 `systemPromptAppend`,避免 resume 时重复污染历史
|
|
17
|
+
- **Agent plugin 启用判定** — `isEnabled` 改为按 `baseagents.<name>` 配置启用,Codex 额外检测 `@openai/codex-sdk` 是否可用,`init` / `agent new` CLI 同步该判定
|
|
18
|
+
- **context-too-long 提示文案** — 按 agent 是否支持 compact 区分提示,retry 后仍超长时清理 renderer 中混入的错误文本(新增 `IMRenderer.stripContextError`)
|
|
19
|
+
|
|
20
|
+
### Bug Fixes
|
|
21
|
+
|
|
22
|
+
- **Codex resume 历史污染** — systemPromptAppend 在 resume 轮次重复拼接到 prompt 头部,导致系统提示被反复写入对话历史
|
|
23
|
+
|
|
3
24
|
## v3.1.2 (2026-05-25)
|
|
4
25
|
|
|
5
26
|
### Improvements
|
package/README.md
CHANGED
|
@@ -243,14 +243,11 @@ evolclaw/
|
|
|
243
243
|
|
|
244
244
|
### 管理员级命令(Admin+ 可用)
|
|
245
245
|
|
|
246
|
-
|
|
246
|
+
**项目**:
|
|
247
247
|
- `/pwd` - 显示当前项目路径
|
|
248
|
-
- `/plist` - 列出所有项目(显示会话空闲时间)
|
|
249
|
-
- `/p <name|path>` - 切换项目(保留会话历史)
|
|
250
|
-
- `/bind <path>` - 绑定新项目目录
|
|
251
248
|
|
|
252
249
|
**Agent 与模型**:
|
|
253
|
-
- `/
|
|
250
|
+
- `/baseagent [name]` - 查看或切换 Agent 后端(claude / codex / gemini)(别名 `/base`)
|
|
254
251
|
- `/model [model]` - 查看或切换模型
|
|
255
252
|
- `/effort [level]` - 查看或切换推理强度(low / medium / high / max / auto)
|
|
256
253
|
- `/perm [mode]` - 查看或切换权限模式(auto / edit / default / readonly)
|
|
@@ -272,7 +269,6 @@ evolclaw/
|
|
|
272
269
|
- `/file <文件路径>` - 发送文件给用户
|
|
273
270
|
- `/restart` - 重启服务(自愈机制)
|
|
274
271
|
- `/repair` - 检查并修复会话
|
|
275
|
-
- `/agentmd [put|set]` - 管理 AUN agent.md(仅 AUN 渠道)
|
|
276
272
|
|
|
277
273
|
## 技术栈
|
|
278
274
|
|
|
@@ -1094,7 +1094,7 @@ export class AgentRunner {
|
|
|
1094
1094
|
export class ClaudeAgentPlugin {
|
|
1095
1095
|
name = 'claude';
|
|
1096
1096
|
isEnabled(agent) {
|
|
1097
|
-
return agent.
|
|
1097
|
+
return !!agent.config.baseagents?.claude;
|
|
1098
1098
|
}
|
|
1099
1099
|
createAgent(agent, callbacks) {
|
|
1100
1100
|
const override = agent.config.baseagents?.claude;
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
*/
|
|
8
8
|
import { resolveOpenaiConfig } from './resolve.js';
|
|
9
9
|
import { logger } from '../utils/logger.js';
|
|
10
|
+
import { execFileSync } from 'child_process';
|
|
10
11
|
import fs from 'fs';
|
|
11
12
|
import path from 'path';
|
|
12
13
|
import os from 'os';
|
|
@@ -17,13 +18,58 @@ const MIME_EXT = {
|
|
|
17
18
|
'image/gif': '.gif',
|
|
18
19
|
'image/webp': '.webp',
|
|
19
20
|
};
|
|
20
|
-
|
|
21
|
-
|
|
21
|
+
const CODEX_CATALOG_FALLBACK = [
|
|
22
|
+
{ slug: 'gpt-5.5', efforts: ['low', 'medium', 'high', 'xhigh'] },
|
|
23
|
+
{ slug: 'gpt-5.4', efforts: ['low', 'medium', 'high', 'xhigh'] },
|
|
24
|
+
{ slug: 'gpt-5.4-mini', efforts: ['low', 'medium', 'high', 'xhigh'] },
|
|
25
|
+
{ slug: 'gpt-5.3-codex', efforts: ['low', 'medium', 'high', 'xhigh'] },
|
|
26
|
+
{ slug: 'gpt-5.2', efforts: ['low', 'medium', 'high', 'xhigh'] },
|
|
27
|
+
];
|
|
28
|
+
let codexCatalogCache = null;
|
|
29
|
+
export function isCodexSdkAvailable() {
|
|
30
|
+
try {
|
|
31
|
+
import.meta.resolve('@openai/codex-sdk');
|
|
32
|
+
return true;
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
function fetchCodexCatalog() {
|
|
39
|
+
if (codexCatalogCache)
|
|
40
|
+
return codexCatalogCache;
|
|
41
|
+
try {
|
|
42
|
+
const output = execFileSync('codex', ['debug', 'models'], {
|
|
43
|
+
encoding: 'utf-8',
|
|
44
|
+
timeout: 5000,
|
|
45
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
46
|
+
});
|
|
47
|
+
const catalog = JSON.parse(output);
|
|
48
|
+
const models = catalog.models
|
|
49
|
+
.filter(m => m.visibility === 'list')
|
|
50
|
+
.map(m => ({
|
|
51
|
+
slug: m.slug,
|
|
52
|
+
efforts: (m.supported_reasoning_levels || []).map(l => l.effort),
|
|
53
|
+
}));
|
|
54
|
+
if (models.length > 0) {
|
|
55
|
+
codexCatalogCache = models;
|
|
56
|
+
return models;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
catch (e) {
|
|
60
|
+
logger.debug(`[CodexRunner] Failed to fetch model catalog, using fallback: ${e}`);
|
|
61
|
+
}
|
|
62
|
+
return CODEX_CATALOG_FALLBACK;
|
|
63
|
+
}
|
|
64
|
+
export function getCodexEfforts(model) {
|
|
65
|
+
const catalog = fetchCodexCatalog();
|
|
66
|
+
const entry = catalog.find(m => m.slug === model);
|
|
67
|
+
return entry?.efforts ?? catalog[0]?.efforts ?? ['low', 'medium', 'high'];
|
|
68
|
+
}
|
|
22
69
|
// ── Codex Runner ──
|
|
23
70
|
export class CodexRunner {
|
|
24
71
|
name = 'codex';
|
|
25
72
|
capabilities = { clear: false, compact: false, fork: false };
|
|
26
|
-
codex = null;
|
|
27
73
|
codexModule = null;
|
|
28
74
|
model;
|
|
29
75
|
effort;
|
|
@@ -39,21 +85,25 @@ export class CodexRunner {
|
|
|
39
85
|
this.effort = this.resolvedConfig.effort;
|
|
40
86
|
this.onSessionIdUpdate = callbacks.onSessionIdUpdate;
|
|
41
87
|
}
|
|
42
|
-
async ensureCodex() {
|
|
43
|
-
if (!this.
|
|
88
|
+
async ensureCodex(sessionId) {
|
|
89
|
+
if (!this.codexModule) {
|
|
44
90
|
const { requireOptional } = await import('../utils/npm-ops.js');
|
|
45
91
|
this.codexModule = await requireOptional('@openai/codex-sdk');
|
|
46
|
-
this.codex = new this.codexModule.Codex({
|
|
47
|
-
apiKey: this.resolvedConfig.apiKey,
|
|
48
|
-
baseUrl: this.resolvedConfig.baseUrl,
|
|
49
|
-
});
|
|
50
92
|
}
|
|
51
|
-
|
|
93
|
+
const codex = new this.codexModule.Codex({
|
|
94
|
+
apiKey: this.resolvedConfig.apiKey,
|
|
95
|
+
baseUrl: this.resolvedConfig.baseUrl,
|
|
96
|
+
env: {
|
|
97
|
+
...process.env,
|
|
98
|
+
EVOLCLAW_SESSION_ID: sessionId,
|
|
99
|
+
},
|
|
100
|
+
});
|
|
101
|
+
return { codex, mod: this.codexModule };
|
|
52
102
|
}
|
|
53
103
|
// ── ModelSwitcher ──
|
|
54
104
|
setModel(model) { this.model = model; }
|
|
55
105
|
getModel() { return this.model; }
|
|
56
|
-
listModels() { return
|
|
106
|
+
listModels() { return fetchCodexCatalog().map(m => m.slug); }
|
|
57
107
|
// ── Effort ──
|
|
58
108
|
setEffort(effort) { this.effort = effort; }
|
|
59
109
|
getEffort() { return this.effort; }
|
|
@@ -94,10 +144,14 @@ export class CodexRunner {
|
|
|
94
144
|
}
|
|
95
145
|
// ── Core: runQuery ──
|
|
96
146
|
async runQuery(sessionId, prompt, projectPath, initialAgentSessionId, images, systemPromptAppend, sessionManager) {
|
|
97
|
-
|
|
98
|
-
process.env.EVOLCLAW_SESSION_ID = sessionId;
|
|
99
|
-
const { codex } = await this.ensureCodex();
|
|
147
|
+
const { codex } = await this.ensureCodex(sessionId);
|
|
100
148
|
let agentSessionId = initialAgentSessionId || this.activeSessions.get(sessionId);
|
|
149
|
+
let fullPrompt = prompt;
|
|
150
|
+
// Only inject system context on the first turn; resumed Codex threads already
|
|
151
|
+
// have that context in history and repeating it will pollute the conversation.
|
|
152
|
+
if (systemPromptAppend && !agentSessionId) {
|
|
153
|
+
fullPrompt = prompt + '\n\n--- [SYSTEM_PROMPT_END] ---\n' + systemPromptAppend;
|
|
154
|
+
}
|
|
101
155
|
const threadOptions = {
|
|
102
156
|
workingDirectory: projectPath,
|
|
103
157
|
model: this.model,
|
|
@@ -116,7 +170,7 @@ export class CodexRunner {
|
|
|
116
170
|
let input;
|
|
117
171
|
if (images?.length) {
|
|
118
172
|
const tmpDir = os.tmpdir();
|
|
119
|
-
const parts = [{ type: 'text', text:
|
|
173
|
+
const parts = [{ type: 'text', text: fullPrompt }];
|
|
120
174
|
for (let i = 0; i < images.length; i++) {
|
|
121
175
|
const img = images[i];
|
|
122
176
|
const ext = MIME_EXT[img.mimeType || ''] || '.jpg';
|
|
@@ -129,7 +183,7 @@ export class CodexRunner {
|
|
|
129
183
|
logger.info(`[CodexRunner] Attached ${images.length} image(s) as local_image`);
|
|
130
184
|
}
|
|
131
185
|
else {
|
|
132
|
-
input =
|
|
186
|
+
input = fullPrompt;
|
|
133
187
|
}
|
|
134
188
|
const { events } = await thread.runStreamed(input, { signal: controller.signal });
|
|
135
189
|
// 包装为 AgentEvent 流
|
|
@@ -302,10 +356,10 @@ export class CodexRunner {
|
|
|
302
356
|
export class CodexAgentPlugin {
|
|
303
357
|
name = 'codex';
|
|
304
358
|
isEnabled(agent) {
|
|
305
|
-
if (agent.baseagent !== 'codex')
|
|
306
|
-
return false;
|
|
307
359
|
if (!agent.config.baseagents?.codex)
|
|
308
360
|
return false;
|
|
361
|
+
if (!isCodexSdkAvailable())
|
|
362
|
+
return false;
|
|
309
363
|
try {
|
|
310
364
|
const override = agent.config.baseagents.codex;
|
|
311
365
|
const syntheticConfig = { agents: { codex: override } };
|
|
@@ -317,8 +371,10 @@ export class CodexAgentPlugin {
|
|
|
317
371
|
}
|
|
318
372
|
}
|
|
319
373
|
createAgent(agent, callbacks) {
|
|
374
|
+
if (!isCodexSdkAvailable()) {
|
|
375
|
+
throw new Error('Missing optional dependency @openai/codex-sdk');
|
|
376
|
+
}
|
|
320
377
|
const override = agent.config.baseagents?.codex;
|
|
321
|
-
const syntheticConfig = { agents: { codex: override } };
|
|
322
378
|
const merged = {
|
|
323
379
|
agents: { codex: { ...(override || {}) } },
|
|
324
380
|
};
|
|
@@ -407,8 +407,6 @@ export class GeminiRunner {
|
|
|
407
407
|
export class GeminiAgentPlugin {
|
|
408
408
|
name = 'gemini';
|
|
409
409
|
isEnabled(agent) {
|
|
410
|
-
if (agent.baseagent !== 'gemini')
|
|
411
|
-
return false;
|
|
412
410
|
const geminiCfg = agent.config.baseagents?.gemini;
|
|
413
411
|
if (!geminiCfg)
|
|
414
412
|
return false;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import fs from 'fs';
|
|
2
2
|
import path from 'path';
|
|
3
|
-
import { kitsDir, eckDebugDir, resolveRoot } from '../paths.js';
|
|
3
|
+
import { kitsDir, eckDebugDir, resolveRoot, getPackageRoot } from '../paths.js';
|
|
4
4
|
import { logger } from '../utils/logger.js';
|
|
5
5
|
// ── Param descriptions (for debug output) ──
|
|
6
6
|
const PARAM_DESCRIPTIONS = {
|
|
@@ -22,13 +22,48 @@ const PARAM_DESCRIPTIONS = {
|
|
|
22
22
|
venueUid: 'venue 唯一标识',
|
|
23
23
|
project: '当前项目目录名(由 CURRENT_PROJECT 派生)',
|
|
24
24
|
sessionName: '会话名称',
|
|
25
|
-
|
|
25
|
+
chatmode: '会话模式(interactive/proactive)',
|
|
26
26
|
readonly: '是否只读模式',
|
|
27
27
|
canSendFile: '当前渠道是否支持发文件',
|
|
28
28
|
capabilities: '渠道能力列表',
|
|
29
29
|
baseAgent: '当前 base agent 规范值(claude/codex/gemini/hermes)',
|
|
30
30
|
baseAgentName: '当前 base agent 显示名',
|
|
31
31
|
};
|
|
32
|
+
function buildPathMappings(vars) {
|
|
33
|
+
const pkgRoot = getPackageRoot();
|
|
34
|
+
const evolHome = String(vars['EVOLCLAW_HOME'] || resolveRoot());
|
|
35
|
+
const selfAid = vars['selfAid'] ? String(vars['selfAid']) : '';
|
|
36
|
+
const currentProject = vars['CURRENT_PROJECT'] ? String(vars['CURRENT_PROJECT']) : '';
|
|
37
|
+
const mappings = [
|
|
38
|
+
{ prefix: path.join(pkgRoot, 'kits', 'rules'), alias: '$KITS_RULES' },
|
|
39
|
+
{ prefix: path.join(pkgRoot, 'kits', 'templates', 'system-fragments'), alias: '$KITS_FRAGMENTS' },
|
|
40
|
+
{ prefix: path.join(pkgRoot, 'kits', 'templates'), alias: '$KITS_TEMPLATES' },
|
|
41
|
+
{ prefix: path.join(pkgRoot, 'kits', 'docs'), alias: '$KITS_DOCS' },
|
|
42
|
+
{ prefix: path.join(pkgRoot, 'kits'), alias: '$KITS' },
|
|
43
|
+
{ prefix: pkgRoot, alias: '$PACKAGE_ROOT' },
|
|
44
|
+
];
|
|
45
|
+
if (selfAid) {
|
|
46
|
+
mappings.push({ prefix: path.join(evolHome, 'agents', selfAid), alias: '$AGENT_DIR' });
|
|
47
|
+
}
|
|
48
|
+
mappings.push({ prefix: evolHome, alias: '$EVOLCLAW_HOME' });
|
|
49
|
+
if (currentProject) {
|
|
50
|
+
mappings.push({ prefix: currentProject, alias: '$CURRENT_PROJECT' });
|
|
51
|
+
}
|
|
52
|
+
// Sort by prefix length descending so longer (more specific) paths match first
|
|
53
|
+
mappings.sort((a, b) => b.prefix.length - a.prefix.length);
|
|
54
|
+
return mappings;
|
|
55
|
+
}
|
|
56
|
+
function shortenPath(filePath, mappings) {
|
|
57
|
+
const normalized = filePath.replace(/\\/g, '/');
|
|
58
|
+
for (const { prefix, alias } of mappings) {
|
|
59
|
+
const normalizedPrefix = prefix.replace(/\\/g, '/');
|
|
60
|
+
if (normalized.startsWith(normalizedPrefix)) {
|
|
61
|
+
const rest = normalized.slice(normalizedPrefix.length);
|
|
62
|
+
return alias + rest;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return filePath;
|
|
66
|
+
}
|
|
32
67
|
// ── Cache ──
|
|
33
68
|
let _manifestCache = null;
|
|
34
69
|
const _sessionPathCache = new Map();
|
|
@@ -49,6 +84,8 @@ export function renderKitSections(ctx) {
|
|
|
49
84
|
loadKitManifest();
|
|
50
85
|
const sections = _manifestCache;
|
|
51
86
|
const fileParts = [];
|
|
87
|
+
const fragmentParts = [];
|
|
88
|
+
const pathMappings = buildPathMappings(ctx.vars);
|
|
52
89
|
for (const section of sections) {
|
|
53
90
|
if (section.enabled === false)
|
|
54
91
|
continue;
|
|
@@ -62,14 +99,20 @@ export function renderKitSections(ctx) {
|
|
|
62
99
|
if (!content.trim())
|
|
63
100
|
continue;
|
|
64
101
|
const label = section.description ? `${section.id} — ${section.description}` : section.id;
|
|
65
|
-
|
|
102
|
+
const displayPath = shortenPath(filePath, pathMappings);
|
|
103
|
+
const part = `Contenu de ${displayPath} (${label}):\n\n${content.trimEnd()}`;
|
|
104
|
+
fileParts.push(part);
|
|
105
|
+
if (section.needsInjection) {
|
|
106
|
+
fragmentParts.push(part);
|
|
107
|
+
}
|
|
66
108
|
}
|
|
67
109
|
}
|
|
68
110
|
if (fileParts.length === 0)
|
|
69
111
|
return '';
|
|
70
112
|
const body = fileParts.join('\n\n');
|
|
71
113
|
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
|
-
|
|
114
|
+
const fragmentsOutput = fragmentParts.length > 0 ? fragmentParts.join('\n\n') : '';
|
|
115
|
+
writeDebugFiles(ctx, output, fragmentsOutput);
|
|
73
116
|
return output;
|
|
74
117
|
}
|
|
75
118
|
export function cleanEckDebug() {
|
|
@@ -234,11 +277,14 @@ function isTruthy(val) {
|
|
|
234
277
|
// CHUNK_CONTINUE_6
|
|
235
278
|
// ── Template rendering ──
|
|
236
279
|
function renderTemplate(template, vars) {
|
|
237
|
-
// Pass 1: conditional sections {{?key=value}}
|
|
238
|
-
let result = template.replace(/\{\{\?(\w+)(
|
|
239
|
-
if (
|
|
240
|
-
return String(vars[key])
|
|
241
|
-
|
|
280
|
+
// Pass 1: conditional sections {{?key=value}}, {{?key!=value}}, {{?key}}...{{/}}
|
|
281
|
+
let result = template.replace(/\{\{\?(\w+)(!=|=)([^}]*)?\}\}([\s\S]*?)\{\{\/\}\}/g, (_match, key, op, value, body) => {
|
|
282
|
+
if (op === '!=')
|
|
283
|
+
return String(vars[key]) !== value ? body : '';
|
|
284
|
+
return String(vars[key]) === value ? body : '';
|
|
285
|
+
});
|
|
286
|
+
// Pass 1b: truthy-only {{?key}}...{{/}}
|
|
287
|
+
result = result.replace(/\{\{\?(\w+)\}\}([\s\S]*?)\{\{\/\}\}/g, (_match, key, body) => {
|
|
242
288
|
return isTruthy(vars[key]) ? body : '';
|
|
243
289
|
});
|
|
244
290
|
// Pass 2: variable substitution {{key}}
|
|
@@ -261,7 +307,7 @@ function getSessionCache(sessionId) {
|
|
|
261
307
|
return cache;
|
|
262
308
|
}
|
|
263
309
|
// ── Debug output ──
|
|
264
|
-
function writeDebugFiles(ctx, output) {
|
|
310
|
+
function writeDebugFiles(ctx, output, fragmentsOutput) {
|
|
265
311
|
const now = new Date();
|
|
266
312
|
const ts = now.toISOString().replace(/[T:.]/g, '-').slice(0, 19);
|
|
267
313
|
const dir = eckDebugDir();
|
|
@@ -278,4 +324,7 @@ function writeDebugFiles(ctx, output) {
|
|
|
278
324
|
};
|
|
279
325
|
fs.writeFile(path.join(dir, `vars-${ts}.json`), JSON.stringify(varsData, null, 2), () => { });
|
|
280
326
|
fs.writeFile(path.join(dir, `context-${ts}.md`), output, () => { });
|
|
327
|
+
if (fragmentsOutput) {
|
|
328
|
+
fs.writeFile(path.join(dir, `fragments-${ts}.md`), fragmentsOutput, () => { });
|
|
329
|
+
}
|
|
281
330
|
}
|
package/dist/aun/aid/agentmd.js
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import fs from 'fs';
|
|
2
2
|
import path from 'path';
|
|
3
|
-
import os from 'os';
|
|
4
3
|
import { getAunClient } from './client.js';
|
|
5
|
-
import { agentMdPath, aidLocalDir } from '../../paths.js';
|
|
4
|
+
import { agentMdPath, aidLocalDir, resolveRoot } from '../../paths.js';
|
|
6
5
|
export function buildInitialAgentMd(opts) {
|
|
7
6
|
const agentName = opts.aid.split('.')[0];
|
|
8
7
|
const agentType = opts.type || 'ai';
|
|
@@ -83,15 +82,16 @@ async function verifyContent(content, aid, certPem, client) {
|
|
|
83
82
|
* Create a bare AUNClient (no createAid) for read-only operations.
|
|
84
83
|
*/
|
|
85
84
|
async function createBareClient(aunPath) {
|
|
85
|
+
const p = aunPath ?? resolveRoot();
|
|
86
86
|
const { AUNClient } = await import('@agentunion/fastaun');
|
|
87
|
-
const caCertPath = path.join(
|
|
88
|
-
const clientOpts = { aun_path:
|
|
87
|
+
const caCertPath = path.join(p, 'CA', 'root', 'root.crt');
|
|
88
|
+
const clientOpts = { aun_path: p, debug: false };
|
|
89
89
|
if (fs.existsSync(caCertPath))
|
|
90
90
|
clientOpts.root_ca_path = caCertPath;
|
|
91
91
|
return new AUNClient(clientOpts);
|
|
92
92
|
}
|
|
93
93
|
export async function agentmdGet(aid, opts) {
|
|
94
|
-
const aunPath = opts?.aunPath ??
|
|
94
|
+
const aunPath = opts?.aunPath ?? resolveRoot();
|
|
95
95
|
const localPath = agentMdPath(aid);
|
|
96
96
|
// === Path A: local agent.md exists ===
|
|
97
97
|
if (fs.existsSync(localPath)) {
|
|
@@ -109,7 +109,8 @@ export async function agentmdGet(aid, opts) {
|
|
|
109
109
|
}
|
|
110
110
|
// Fallback: local invalid → try remote
|
|
111
111
|
try {
|
|
112
|
-
const
|
|
112
|
+
const info = await client.fetchAgentMd(aid);
|
|
113
|
+
const remote = info.content;
|
|
113
114
|
if (remote) {
|
|
114
115
|
const remoteVerification = await verifyContent(remote, aid, certPem, client);
|
|
115
116
|
if (remoteVerification.status === 'verified') {
|
|
@@ -133,20 +134,13 @@ export async function agentmdGet(aid, opts) {
|
|
|
133
134
|
const client = opts?.client ?? await createBareClient(aunPath);
|
|
134
135
|
const ownClient = !opts?.client;
|
|
135
136
|
try {
|
|
136
|
-
const
|
|
137
|
+
const info = await client.fetchAgentMd(aid);
|
|
138
|
+
const raw = info.content;
|
|
137
139
|
if (!opts?.withVerification) {
|
|
138
|
-
// Persist without verification
|
|
139
|
-
const aidDir = aidLocalDir(aid);
|
|
140
|
-
fs.mkdirSync(aidDir, { recursive: true });
|
|
141
|
-
fs.writeFileSync(path.join(aidDir, 'agent.md'), raw, 'utf-8');
|
|
142
140
|
return raw;
|
|
143
141
|
}
|
|
144
142
|
const certPem = await obtainCertPem(aid, aunPath, client);
|
|
145
143
|
const verification = await verifyContent(raw, aid, certPem, client);
|
|
146
|
-
// Persist to local
|
|
147
|
-
const aidDir = aidLocalDir(aid);
|
|
148
|
-
fs.mkdirSync(aidDir, { recursive: true });
|
|
149
|
-
fs.writeFileSync(path.join(aidDir, 'agent.md'), raw, 'utf-8');
|
|
150
144
|
return { content: raw, verification };
|
|
151
145
|
}
|
|
152
146
|
finally {
|
|
@@ -158,24 +152,52 @@ export async function agentmdGet(aid, opts) {
|
|
|
158
152
|
}
|
|
159
153
|
}
|
|
160
154
|
/**
|
|
161
|
-
* Upload agent.md: auto-sign + upload
|
|
155
|
+
* Upload agent.md: write to local file → publishAgentMd (auto-sign + upload).
|
|
162
156
|
*/
|
|
163
157
|
export async function agentmdPut(content, opts) {
|
|
164
|
-
const aunPath = opts.aunPath ??
|
|
158
|
+
const aunPath = opts.aunPath ?? resolveRoot();
|
|
165
159
|
const client = opts.client ?? await getAunClient(opts.aid, { aunPath });
|
|
166
160
|
const ownClient = !opts.client;
|
|
161
|
+
const dir = aidLocalDir(opts.aid);
|
|
162
|
+
const filePath = path.join(dir, 'agent.md');
|
|
163
|
+
const existed = fs.existsSync(filePath);
|
|
167
164
|
try {
|
|
168
|
-
let signed;
|
|
169
|
-
try {
|
|
170
|
-
signed = await client.auth.signAgentMd(content);
|
|
171
|
-
}
|
|
172
|
-
catch {
|
|
173
|
-
signed = content;
|
|
174
|
-
}
|
|
175
|
-
await client.auth.uploadAgentMd(signed);
|
|
176
|
-
const dir = aidLocalDir(opts.aid);
|
|
177
165
|
fs.mkdirSync(dir, { recursive: true });
|
|
178
|
-
fs.writeFileSync(
|
|
166
|
+
fs.writeFileSync(filePath, content, 'utf-8');
|
|
167
|
+
await client.publishAgentMd();
|
|
168
|
+
}
|
|
169
|
+
catch (e) {
|
|
170
|
+
if (!existed)
|
|
171
|
+
try {
|
|
172
|
+
fs.unlinkSync(filePath);
|
|
173
|
+
}
|
|
174
|
+
catch { /* ignore */ }
|
|
175
|
+
throw e;
|
|
176
|
+
}
|
|
177
|
+
finally {
|
|
178
|
+
if (ownClient)
|
|
179
|
+
try {
|
|
180
|
+
await client.close();
|
|
181
|
+
}
|
|
182
|
+
catch { /* ignore */ }
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Check if agent.md is up-to-date (30-day cache), fetch if changed.
|
|
187
|
+
* Returns changed=true + content when a new version was downloaded.
|
|
188
|
+
*/
|
|
189
|
+
export async function agentmdSync(aid, opts) {
|
|
190
|
+
const client = opts?.client ?? await createBareClient();
|
|
191
|
+
const ownClient = !opts?.client;
|
|
192
|
+
try {
|
|
193
|
+
const state = await client.checkAgentMd(aid, 30);
|
|
194
|
+
if (!state.in_sync || !state.local_found) {
|
|
195
|
+
const info = await client.fetchAgentMd(aid);
|
|
196
|
+
return { changed: true, content: info.content };
|
|
197
|
+
}
|
|
198
|
+
const localPath = agentMdPath(aid);
|
|
199
|
+
const content = fs.existsSync(localPath) ? fs.readFileSync(localPath, 'utf-8') : undefined;
|
|
200
|
+
return { changed: false, content };
|
|
179
201
|
}
|
|
180
202
|
finally {
|
|
181
203
|
if (ownClient)
|
package/dist/aun/aid/index.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
export { isValidAid, aidList, aidCreate, aidShow, aidDelete, aidLookup, appendAidLifecycle, readAidLifecycle } from './identity.js';
|
|
2
|
-
export { buildInitialAgentMd, agentmdGet, agentmdPut } from './agentmd.js';
|
|
2
|
+
export { buildInitialAgentMd, agentmdGet, agentmdPut, agentmdSync } from './agentmd.js';
|
|
3
3
|
export { MIN_AUN_CORE_SDK, AUN_CORE_SDK_PKG, isAunSdkVersionOk, resolveAunCoreSdkPkg, ensureAunSdk, isAunSdkReady, downloadCaRoot, getAunClient, suppressSdkLogs, } from './client.js';
|
|
@@ -6,9 +6,12 @@ export async function createShortConnection(aid, opts) {
|
|
|
6
6
|
const slotId = opts?.slotId ?? '';
|
|
7
7
|
const caCertPath = path.join(aunPath, 'CA', 'root', 'root.crt');
|
|
8
8
|
const { AUNClient } = await import('@agentunion/fastaun');
|
|
9
|
+
const encryptionSeed = process.env.AUN_ENCRYPTION_SEED || undefined;
|
|
9
10
|
const clientOpts = { aun_path: aunPath, debug: false };
|
|
10
11
|
if (fs.existsSync(caCertPath))
|
|
11
12
|
clientOpts.root_ca_path = caCertPath;
|
|
13
|
+
if (encryptionSeed)
|
|
14
|
+
clientOpts.encryption_seed = encryptionSeed;
|
|
12
15
|
const client = new AUNClient(clientOpts);
|
|
13
16
|
await client.auth.createAid({ aid });
|
|
14
17
|
const authResult = await client.auth.authenticate({ aid });
|