evolcore 0.0.24 → 0.0.26
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 +59 -0
- package/bin/ec.js +12 -1
- package/bin/install-codex-managed-hooks.mjs +28 -1
- package/dist/agents/baseagent.js +96 -46
- package/dist/agents/claude-runner.js +54 -13
- package/dist/agents/codex/app-server-client.js +13 -5
- package/dist/agents/codex/catalog.js +1 -1
- package/dist/agents/codex/diagnostics.js +34 -1
- package/dist/agents/codex-runner.js +128 -42
- package/dist/agents/ecagent-runner.js +14 -18
- package/dist/agents/gemini-runner.js +24 -9
- package/dist/agents/runner-types.js +25 -17
- package/dist/aun/aid/agentmd.js +135 -10
- package/dist/aun/aid/client.js +1 -1
- package/dist/aun/aid/identity.js +1 -1
- package/dist/aun/aid/index.js +2 -1
- package/dist/aun/aid/managed-operation.js +53 -1
- package/dist/aun/aid/store.js +17 -13
- package/dist/aun/menu-client.js +74 -0
- package/dist/aun/msg/group.js +1 -1
- package/dist/aun/msg/managed-operation.js +1 -1
- package/dist/aun/msg/p2p.js +6 -1
- package/dist/aun/msg/upload.js +30 -22
- package/dist/aun/storage/client.js +4 -1
- package/dist/aun/storage/index.js +1 -1
- package/dist/channels/aun.js +329 -94
- package/dist/cli/agent-command.js +125 -5
- package/dist/cli/agent-create.js +151 -11
- package/dist/cli/agent.js +119 -6
- package/dist/cli/aun-commands.js +74 -7
- package/dist/cli/config.js +27 -8
- package/dist/cli/contact.js +63 -6
- package/dist/cli/daemon-commands.js +90 -25
- package/dist/cli/diag.js +31 -0
- package/dist/cli/handoff-command.js +35 -3
- package/dist/cli/index.js +7 -1
- package/dist/cli/managed-command-guard.js +3 -0
- package/dist/cli/menu-client.js +38 -23
- package/dist/cli/restart-monitor.js +57 -10
- package/dist/config/builtin-roles.js +2 -0
- package/dist/config/config-manager.js +15 -0
- package/dist/config/config-operation-service.js +165 -3
- package/dist/config/contact-operation-service.js +96 -7
- package/dist/config/resolved-config-op.js +66 -8
- package/dist/core/agent-application-service.js +2 -1
- package/dist/core/auth/authorization-audit.js +20 -12
- package/dist/core/auth/operation-authorizer.js +87 -8
- package/dist/core/auth/operation-catalog.js +51 -4
- package/dist/core/auth/remote-access-store.js +40 -1
- package/dist/core/bootstrap-service.js +1 -1
- package/dist/core/command/agent-control.js +54 -25
- package/dist/core/command/cli-intent-parser.js +18 -0
- package/dist/core/command/command-handler.js +152 -20
- package/dist/core/command/diag-service.js +118 -0
- package/dist/core/command/menu-catalog.js +13 -3
- package/dist/core/command/menu-handler.js +88 -31
- package/dist/core/command/menu-protocol.js +6 -0
- package/dist/core/command/menu-token-store.js +41 -8
- package/dist/core/command/slash-gate.js +34 -4
- package/dist/core/command/slash-handler.js +77 -12
- package/dist/core/event-catalog.js +5 -0
- package/dist/core/evolagent.js +3 -3
- package/dist/core/handoff/runtime.js +205 -27
- package/dist/core/handoff/store.js +193 -4
- package/dist/core/message/im-renderer.js +72 -21
- package/dist/core/message/message-bridge.js +69 -9
- package/dist/core/message/message-queue.js +26 -0
- package/dist/core/message/message-utils.js +1 -0
- package/dist/core/message/response-engine.js +48 -23
- package/dist/core/model/catalog-service.js +79 -49
- package/dist/core/model/context-window-suffix.js +33 -0
- package/dist/core/model/model-catalog.js +1 -1
- package/dist/core/model/model-permission.js +12 -3
- package/dist/core/permission/ec-command-parser.js +51 -12
- package/dist/core/permission/sandbox-runtime.js +36 -14
- package/dist/core/permission/tool-policy.js +47 -7
- package/dist/index.js +195 -31
- package/dist/ipc.js +54 -2
- package/dist/response-system/engines/v1/proactive-flow.js +59 -2
- package/dist/stats/billing.js +41 -15
- package/dist/stats/price-resolver.js +28 -10
- package/dist/utils/avatar-upload.js +282 -23
- package/dist/utils/cross-platform.js +30 -1
- package/dist/utils/error-utils.js +21 -0
- package/dist/utils/evolcore-version.js +129 -0
- package/dist/utils/logger.js +3 -1
- package/dist/utils/model-prices.jsonl +3 -3
- package/dist/utils/npm-ops.js +176 -35
- package/kits/docs/context-assembly.md +1 -0
- package/kits/docs/evolcore/INDEX.md +3 -1
- package/kits/docs/evolcore/agent-create.md +30 -11
- package/kits/docs/evolcore/agent.md +18 -8
- package/kits/docs/evolcore/aid.md +6 -4
- package/kits/docs/evolcore/codex-windows-sandbox.md +146 -0
- package/kits/docs/evolcore/config.md +7 -0
- package/kits/docs/evolcore/contact.md +12 -5
- package/kits/docs/evolcore/menu-client.md +11 -3
- package/kits/docs/evolcore/msg.md +1 -0
- package/kits/docs/prompt-loading-architecture.md +1 -0
- package/kits/docs/venues/group.md +12 -0
- package/kits/eck_manifest.json +12 -0
- package/kits/rules/03-identity.md +3 -1
- package/kits/templates/message-fragments/handoff-request-to-target.md +3 -3
- package/kits/templates/roles/admin.json +14 -2
- package/kits/templates/roles/member.json +3 -2
- package/kits/templates/roles/visitor.json +4 -3
- package/kits/templates/system-fragments/bootstrap.md +16 -5
- package/kits/templates/system-fragments/channel.md +1 -1
- package/kits/templates/system-fragments/session.md +1 -1
- package/package.json +6 -4
- package/skills/eclink/SKILL.md +0 -84
- package/skills/eclink/agents/openai.yaml +0 -4
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import path from 'path';
|
|
2
2
|
import { getArgValue, isHelpFlag, wantsHelp } from './help.js';
|
|
3
3
|
import { isHostChinese } from '../utils/locale.js';
|
|
4
|
+
import { readTaskRuntimeContextFromEnv } from './task-context.js';
|
|
4
5
|
function formatTimeAgo(ms) {
|
|
5
6
|
const sec = Math.floor(ms / 1000);
|
|
6
7
|
if (sec < 60)
|
|
@@ -25,7 +26,7 @@ function formatBytes(bytes) {
|
|
|
25
26
|
export async function cmdAgent(args) {
|
|
26
27
|
const sub = args[0];
|
|
27
28
|
const formatJson = getArgValue(args, '--format') === 'json';
|
|
28
|
-
const commands = new Set(['list', 'show', 'new', 'enable', 'disable', 'get', 'set', 'rename', 'ready', 'reload', 'delete']);
|
|
29
|
+
const commands = new Set(['list', 'show', 'new', 'enable', 'disable', 'get', 'set', 'persona', 'rename', 'ready', 'reload', 'delete']);
|
|
29
30
|
if (sub === 'create') {
|
|
30
31
|
const error = { ok: false, code: 'INVALID_COMMAND', error: '`ec agent create` is not a command; use `ec agent new`.' };
|
|
31
32
|
if (formatJson)
|
|
@@ -47,6 +48,9 @@ Commands:
|
|
|
47
48
|
disable <aid> 停用 agent
|
|
48
49
|
get <aid> <key> 读取单个配置字段(支持点路径)
|
|
49
50
|
set <aid> <key> <val> 修改单个配置字段(支持点路径)
|
|
51
|
+
persona show 查看当前 Agent 的 persona.md
|
|
52
|
+
persona --text <text> 用文本设置当前 Agent 的 persona.md
|
|
53
|
+
persona --file <path> 用 .md/.txt 文件设置当前 Agent 的 persona.md
|
|
50
54
|
rename <aid> <name> 修改 agent.md 中的显示名称
|
|
51
55
|
ready <aid> 标记 bootstrap 完成,进入 active 状态
|
|
52
56
|
reload [aid] 热重载配置(无参数=全量 resync)
|
|
@@ -63,14 +67,15 @@ Options:
|
|
|
63
67
|
ec agent enable mybot.agentid.pub
|
|
64
68
|
ec agent get mybot.agentid.pub active_baseagent
|
|
65
69
|
ec agent set mybot.agentid.pub active_baseagent codex
|
|
70
|
+
ec agent persona --text "你是一名严谨的诊断工程师。"
|
|
66
71
|
ec agent rename mybot.agentid.pub "New Name"
|
|
67
72
|
ec agent ready mybot.agentid.pub
|
|
68
73
|
ec agent delete mybot.agentid.pub --purge`);
|
|
69
74
|
return;
|
|
70
75
|
}
|
|
71
|
-
const { agentList, agentShow, agentCreateInteractive, agentCreateNonInteractive, agentReload, agentEnable, agentDisable, agentGet, agentSet, agentRename, agentDelete, agentReady, runManagedAgentOperation, } = await import('./agent.js');
|
|
72
|
-
const { parseAgentOwnerArgs, resolveAgentCreatePlan, validateAgentCreatePlan } = await import('./agent-create.js');
|
|
73
|
-
if (process.env.EVOLCORE_SESSION_ID && !wantsHelp(args) && !new Set(['get', 'set', 'ready']).has(sub)) {
|
|
76
|
+
const { agentList, agentShow, agentCreateInteractive, agentCreateNonInteractive, agentReload, agentEnable, agentDisable, agentGet, agentSet, agentPersona, agentRename, agentDelete, agentReady, runManagedAgentOperation, } = await import('./agent.js');
|
|
77
|
+
const { parseAgentOwnerArgs, parseAgentTags, resolveAgentCreatePlan, validateAgentCreatePlan } = await import('./agent-create.js');
|
|
78
|
+
if (process.env.EVOLCORE_SESSION_ID && !wantsHelp(args) && !new Set(['get', 'set', 'persona', 'ready']).has(sub)) {
|
|
74
79
|
if (sub === 'new' && !args.includes('--non-interactive')) {
|
|
75
80
|
const error = {
|
|
76
81
|
ok: false,
|
|
@@ -215,6 +220,8 @@ Options:
|
|
|
215
220
|
--handoff <context> 当前任务的 Bootstrap 上下文
|
|
216
221
|
--name <display-name> 默认: package.json name,否则目录名
|
|
217
222
|
--description <text> 默认: package.json description,否则使用 EvolCore 默认描述
|
|
223
|
+
--type <type> 主体类型: human|agent|group|service|device|org(默认 agent)
|
|
224
|
+
--tags <json-array> 3 到 6 个标签,例如 '["coding","diagnostics","logs"]'
|
|
218
225
|
--dry-run 只预览最终参数,不注册 AID、不写文件
|
|
219
226
|
--force 覆盖已有 config.json
|
|
220
227
|
--format json 输出 JSON
|
|
@@ -259,6 +266,19 @@ Options:
|
|
|
259
266
|
process.exitCode = 1;
|
|
260
267
|
return;
|
|
261
268
|
}
|
|
269
|
+
let tags;
|
|
270
|
+
try {
|
|
271
|
+
tags = parseAgentTags(getArg('--tags'));
|
|
272
|
+
}
|
|
273
|
+
catch (error) {
|
|
274
|
+
const result = { ok: false, error: error instanceof Error ? error.message : String(error) };
|
|
275
|
+
if (formatJson)
|
|
276
|
+
console.log(JSON.stringify(result));
|
|
277
|
+
else
|
|
278
|
+
console.error(`❌ ${result.error}`);
|
|
279
|
+
process.exitCode = 1;
|
|
280
|
+
return;
|
|
281
|
+
}
|
|
262
282
|
const plan = resolveAgentCreatePlan({
|
|
263
283
|
aid: name,
|
|
264
284
|
project: getArg('--project'),
|
|
@@ -267,6 +287,8 @@ Options:
|
|
|
267
287
|
handoff: getArg('--handoff'),
|
|
268
288
|
name: getArg('--name'),
|
|
269
289
|
description: getArg('--description'),
|
|
290
|
+
type: getArg('--type'),
|
|
291
|
+
tags,
|
|
270
292
|
force: args.includes('--force'),
|
|
271
293
|
});
|
|
272
294
|
const validationError = validateAgentCreatePlan(plan);
|
|
@@ -304,6 +326,8 @@ Options:
|
|
|
304
326
|
owners: plan.owners,
|
|
305
327
|
name: plan.name,
|
|
306
328
|
description: plan.description,
|
|
329
|
+
type: plan.type,
|
|
330
|
+
tags: plan.tags,
|
|
307
331
|
force: args.includes('--force'),
|
|
308
332
|
});
|
|
309
333
|
if (!result.ok) {
|
|
@@ -330,6 +354,15 @@ Options:
|
|
|
330
354
|
: result.hotLoadError
|
|
331
355
|
? ` ✗ 热重载失败:${result.hotLoadError}`
|
|
332
356
|
: ' ⚠ 服务未运行,下次 ec start 时生效');
|
|
357
|
+
if (result.ownerContactsSynced?.length) {
|
|
358
|
+
console.log(` ✓ 已同步 Owner 联系人: ${result.ownerContactsSynced.join(', ')}`);
|
|
359
|
+
}
|
|
360
|
+
if (result.ownerContactsSkipped?.length) {
|
|
361
|
+
console.log(` ⚠ 无本地联系人簿,跳过 Owner: ${result.ownerContactsSkipped.join(', ')}`);
|
|
362
|
+
}
|
|
363
|
+
if (result.ownerContactSyncErrors?.length) {
|
|
364
|
+
console.log(` ⚠ Owner 联系人同步失败: ${result.ownerContactSyncErrors.join('; ')}`);
|
|
365
|
+
}
|
|
333
366
|
if (result.ownerBoundAid) {
|
|
334
367
|
console.log(` ✓ agent owner 已绑定: ${result.ownerBoundAid}`);
|
|
335
368
|
}
|
|
@@ -364,6 +397,15 @@ Options:
|
|
|
364
397
|
: result.hotLoadError
|
|
365
398
|
? ` ✗ 热重载失败:${result.hotLoadError}`
|
|
366
399
|
: ' ⚠ 服务未运行,下次 ec start 时生效');
|
|
400
|
+
if (result.ownerContactsSynced?.length) {
|
|
401
|
+
console.log(` ✓ 已同步 Owner 联系人: ${result.ownerContactsSynced.join(', ')}`);
|
|
402
|
+
}
|
|
403
|
+
if (result.ownerContactsSkipped?.length) {
|
|
404
|
+
console.log(` ⚠ 无本地联系人簿,跳过 Owner: ${result.ownerContactsSkipped.join(', ')}`);
|
|
405
|
+
}
|
|
406
|
+
if (result.ownerContactSyncErrors?.length) {
|
|
407
|
+
console.log(` ⚠ Owner 联系人同步失败: ${result.ownerContactSyncErrors.join('; ')}`);
|
|
408
|
+
}
|
|
367
409
|
if (result.ownerBoundAid) {
|
|
368
410
|
console.log(` ✓ agent owner 已绑定: ${result.ownerBoundAid}`);
|
|
369
411
|
}
|
|
@@ -497,6 +539,84 @@ Options:
|
|
|
497
539
|
return;
|
|
498
540
|
}
|
|
499
541
|
// --- get ---
|
|
542
|
+
if (sub === 'persona') {
|
|
543
|
+
if (wantsHelp(args)) {
|
|
544
|
+
console.log(`用法: ec agent persona show [--format json]
|
|
545
|
+
ec agent persona --text <text> [--format json]
|
|
546
|
+
ec agent persona --file <path> [--format json]
|
|
547
|
+
|
|
548
|
+
读取或设置当前 Agent 的 persona.md。--file 只接受 .md/.txt 文本文件。`);
|
|
549
|
+
return;
|
|
550
|
+
}
|
|
551
|
+
const formatSuffix = args.length >= 3
|
|
552
|
+
&& args.at(-2) === '--format'
|
|
553
|
+
&& args.at(-1) === 'json';
|
|
554
|
+
const commandArgs = formatSuffix ? args.slice(0, -2) : args;
|
|
555
|
+
const personaArgs = commandArgs.slice(1);
|
|
556
|
+
const invalid = (error) => {
|
|
557
|
+
const result = { ok: false, code: 'INVALID_ARGS', error };
|
|
558
|
+
if (formatJson)
|
|
559
|
+
console.log(JSON.stringify(result));
|
|
560
|
+
else
|
|
561
|
+
console.error(`❌ ${result.error}`);
|
|
562
|
+
process.exitCode = 1;
|
|
563
|
+
return;
|
|
564
|
+
};
|
|
565
|
+
if (!formatSuffix && args.includes('--format')) {
|
|
566
|
+
invalid('persona 仅支持末尾的 --format json');
|
|
567
|
+
return;
|
|
568
|
+
}
|
|
569
|
+
let text;
|
|
570
|
+
let file;
|
|
571
|
+
if (personaArgs.length === 1 && personaArgs[0] === 'show') {
|
|
572
|
+
// show is the only read form; no payload is accepted.
|
|
573
|
+
}
|
|
574
|
+
else if (personaArgs.length === 2 && personaArgs[0] === '--text') {
|
|
575
|
+
text = personaArgs[1];
|
|
576
|
+
if (text === undefined) {
|
|
577
|
+
invalid('persona --text 缺少参数');
|
|
578
|
+
return;
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
else if (personaArgs.length === 2 && personaArgs[0] === '--file') {
|
|
582
|
+
file = personaArgs[1];
|
|
583
|
+
if (!file || file.startsWith('-')) {
|
|
584
|
+
invalid('persona --file 缺少有效路径');
|
|
585
|
+
return;
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
else {
|
|
589
|
+
invalid('用法: ec agent persona show | --text <text> | --file <path>');
|
|
590
|
+
return;
|
|
591
|
+
}
|
|
592
|
+
const taskAid = process.env.EVOLCORE_SESSION_ID ? (readTaskRuntimeContextFromEnv()?.selfAid || '') : '';
|
|
593
|
+
const aid = taskAid || process.env.EVOLCORE_SELF_AID || '';
|
|
594
|
+
if (!aid) {
|
|
595
|
+
const result = { ok: false, code: 'INVALID_ARGS', error: '无法确定当前 Agent AID' };
|
|
596
|
+
if (formatJson)
|
|
597
|
+
console.log(JSON.stringify(result));
|
|
598
|
+
else
|
|
599
|
+
console.error(`❌ ${result.error}`);
|
|
600
|
+
process.exitCode = 1;
|
|
601
|
+
return;
|
|
602
|
+
}
|
|
603
|
+
const result = await agentPersona(aid, { text, file });
|
|
604
|
+
if (!result.ok) {
|
|
605
|
+
if (formatJson)
|
|
606
|
+
console.log(JSON.stringify(result));
|
|
607
|
+
else
|
|
608
|
+
console.error(`❌ ${result.error}`);
|
|
609
|
+
process.exitCode = 1;
|
|
610
|
+
return;
|
|
611
|
+
}
|
|
612
|
+
if (formatJson)
|
|
613
|
+
console.log(JSON.stringify(result, null, 2));
|
|
614
|
+
else if (result.saved)
|
|
615
|
+
console.log(`✓ ${aid} persona.md 已保存`);
|
|
616
|
+
else
|
|
617
|
+
console.log(result.content ?? '(persona.md 不存在)');
|
|
618
|
+
return;
|
|
619
|
+
}
|
|
500
620
|
if (sub === 'get') {
|
|
501
621
|
if (wantsHelp(args)) {
|
|
502
622
|
console.log(`用法: ec agent get <aid> <key> [--format json]
|
|
@@ -720,7 +840,7 @@ Options:
|
|
|
720
840
|
printAgentShowHuman(result);
|
|
721
841
|
}
|
|
722
842
|
function printAgentShowHuman(result) {
|
|
723
|
-
console.log(`${result.aid} (${result.status})\n`);
|
|
843
|
+
console.log(`${result.aid} (${result.status}, lifecycle=${result.lifecycle || 'unknown'})\n`);
|
|
724
844
|
if (result.identity.name || result.identity.description) {
|
|
725
845
|
console.log(' Identity');
|
|
726
846
|
if (result.identity.name)
|
package/dist/cli/agent-create.js
CHANGED
|
@@ -7,12 +7,33 @@ import { ConfigTarget, write as cfgWrite, ensureFile as cfgEnsure, read as cfgRe
|
|
|
7
7
|
import { ipcQuery } from '../ipc.js';
|
|
8
8
|
import { CONFIG_SCHEMA_VERSION } from '../types.js';
|
|
9
9
|
import { ensureInitialRoleRegistry } from '../config/role-service.js';
|
|
10
|
+
import { addContact } from '../config/contact-request-service.js';
|
|
10
11
|
import { getCodexAppServerAvailability } from '../agents/codex-runner.js';
|
|
11
12
|
import { resolveEcagentConfig } from '../agents/baseagent.js';
|
|
12
13
|
import { AUTO_BASEAGENT_CANDIDATES, BASEAGENT_CANDIDATES, detectAvailableBaseagents, } from '../agents/baseagent-availability.js';
|
|
13
14
|
import { agentProjectRootFromDefaults, deriveAgentProjectPath } from '../utils/project-path.js';
|
|
14
15
|
import { isValidAid } from '../aun/aid/validation.js';
|
|
15
|
-
import { DEFAULT_AGENT_DESCRIPTION } from '../aun/aid/agentmd.js';
|
|
16
|
+
import { AGENT_DESCRIPTION_MAX_LENGTH, AGENT_TAG_MAX_COUNT, AGENT_TAG_MIN_COUNT, DEFAULT_AGENT_DESCRIPTION, DEFAULT_AGENT_TYPE, normalizeAgentType, validateAgentTags, validateAgentType, } from '../aun/aid/agentmd.js';
|
|
17
|
+
export function parseAgentTags(raw) {
|
|
18
|
+
if (raw === undefined)
|
|
19
|
+
return undefined;
|
|
20
|
+
let parsed;
|
|
21
|
+
try {
|
|
22
|
+
parsed = JSON.parse(raw);
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
throw new Error('--tags must be a JSON array of 3 to 6 valid tags');
|
|
26
|
+
}
|
|
27
|
+
if (!Array.isArray(parsed))
|
|
28
|
+
throw new Error('--tags must be a JSON array of 3 to 6 valid tags');
|
|
29
|
+
const tags = parsed.map(value => typeof value === 'string' ? value.trim() : value);
|
|
30
|
+
const tagErrors = validateAgentTags(tags, { minCount: AGENT_TAG_MIN_COUNT, maxCount: AGENT_TAG_MAX_COUNT });
|
|
31
|
+
if (tagErrors.length > 0)
|
|
32
|
+
throw new Error(`--tags ${tagErrors.join('; ')}`);
|
|
33
|
+
// Duplicate labels are harmless at the wire level, but should not consume
|
|
34
|
+
// the advertised 3-6 tag budget or produce duplicate frontmatter entries.
|
|
35
|
+
return [...new Set(tags)];
|
|
36
|
+
}
|
|
16
37
|
export function validateAgentOwnerOptions(options) {
|
|
17
38
|
if (options.owner !== undefined && options.owners !== undefined) {
|
|
18
39
|
return 'Options --owner and --owners are mutually exclusive';
|
|
@@ -69,6 +90,42 @@ function withStaticOwners(config, ownersInput) {
|
|
|
69
90
|
function withStaticOwner(config, owner) {
|
|
70
91
|
return withStaticOwners(config, owner ? [owner] : undefined);
|
|
71
92
|
}
|
|
93
|
+
/**
|
|
94
|
+
* An Agent owner must be able to receive the new Agent's Bootstrap reply.
|
|
95
|
+
* Contact admission is per local Agent, so materialize the new AID in each
|
|
96
|
+
* locally managed owner's contact book immediately after creation. Human or
|
|
97
|
+
* remote owners have no local contact book and are reported as skipped.
|
|
98
|
+
*/
|
|
99
|
+
export async function syncOwnerContactsAfterCreate(createdAid, owners) {
|
|
100
|
+
const synced = [];
|
|
101
|
+
const skipped = [];
|
|
102
|
+
const errors = [];
|
|
103
|
+
for (const ownerAid of [...new Set((owners ?? []).map(value => value.trim()).filter(Boolean))]) {
|
|
104
|
+
if (ownerAid === createdAid)
|
|
105
|
+
continue;
|
|
106
|
+
const ownerConfig = path.join(resolvePaths().agentsDir, ownerAid, 'config.json');
|
|
107
|
+
if (!fs.existsSync(ownerConfig)) {
|
|
108
|
+
skipped.push(ownerAid);
|
|
109
|
+
continue;
|
|
110
|
+
}
|
|
111
|
+
try {
|
|
112
|
+
await addContact({
|
|
113
|
+
selfAid: ownerAid,
|
|
114
|
+
primaryId: createdAid,
|
|
115
|
+
actorId: 'agent-create-owner-sync',
|
|
116
|
+
});
|
|
117
|
+
synced.push(ownerAid);
|
|
118
|
+
}
|
|
119
|
+
catch (error) {
|
|
120
|
+
errors.push(`${ownerAid}: ${error instanceof Error ? error.message : String(error)}`);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
return {
|
|
124
|
+
...(synced.length > 0 ? { ownerContactsSynced: synced } : {}),
|
|
125
|
+
...(skipped.length > 0 ? { ownerContactsSkipped: skipped } : {}),
|
|
126
|
+
...(errors.length > 0 ? { ownerContactSyncErrors: errors } : {}),
|
|
127
|
+
};
|
|
128
|
+
}
|
|
72
129
|
function pickDefaultBaseagent(available) {
|
|
73
130
|
return AUTO_BASEAGENT_CANDIDATES.find(candidate => available.includes(candidate)) ?? null;
|
|
74
131
|
}
|
|
@@ -117,13 +174,25 @@ export function resolveAgentCreatePlan(options) {
|
|
|
117
174
|
const owner = options.owner?.trim() || (owners === undefined ? daemon.owners?.[0] : undefined);
|
|
118
175
|
const ownerSource = owners !== undefined || options.owner?.trim() ? 'argument' : owner ? 'daemon' : 'none';
|
|
119
176
|
const name = options.name?.trim() || metadata.name || projectSlug(project);
|
|
120
|
-
|
|
177
|
+
// description 是可选字段:用户显式传入的必须合规(超长会在校验阶段被拒);
|
|
178
|
+
// 从 package.json 推导出来的超长值直接弃用、回落默认短描述——用户没设过它,
|
|
179
|
+
// 不该因此让创建命令失败。
|
|
180
|
+
const explicitDescription = options.description?.trim();
|
|
181
|
+
const packageDescription = metadata.description
|
|
182
|
+
&& Array.from(metadata.description).length <= AGENT_DESCRIPTION_MAX_LENGTH
|
|
183
|
+
? metadata.description
|
|
184
|
+
: undefined;
|
|
185
|
+
const description = explicitDescription || packageDescription || DEFAULT_AGENT_DESCRIPTION;
|
|
186
|
+
// 大小写不敏感:能识别就收敛成小写;无法识别时保留原值,交给 validateAgentCreatePlan 报错。
|
|
187
|
+
const requestedType = options.type?.trim();
|
|
188
|
+
const type = requestedType ? (normalizeAgentType(requestedType) ?? requestedType) : DEFAULT_AGENT_TYPE;
|
|
121
189
|
const configExists = isValidAid(aid) && fs.existsSync(path.join(resolvePaths().agentsDir, aid, 'config.json'));
|
|
122
190
|
return {
|
|
123
|
-
aid, project, baseagent, owner, owners, handoff: options.handoff, name, description,
|
|
191
|
+
aid, project, baseagent, owner, owners, handoff: options.handoff, name, description, type, tags: options.tags,
|
|
124
192
|
force: options.force === true, configExists, baseagentSource, ownerSource,
|
|
125
193
|
nameSource: options.name?.trim() ? 'argument' : metadata.name ? 'package' : 'directory',
|
|
126
|
-
descriptionSource:
|
|
194
|
+
descriptionSource: explicitDescription ? 'argument' : packageDescription ? 'package' : 'default',
|
|
195
|
+
typeSource: options.type?.trim() ? 'argument' : 'default',
|
|
127
196
|
};
|
|
128
197
|
}
|
|
129
198
|
export function validateAgentCreatePlan(plan) {
|
|
@@ -134,6 +203,21 @@ export function validateAgentCreatePlan(plan) {
|
|
|
134
203
|
return `Invalid AID "${plan.aid}": must be a valid multi-level domain (e.g. mybot.agentid.pub)`;
|
|
135
204
|
if (plan.configExists && !plan.force)
|
|
136
205
|
return `Agent "${plan.aid}" already exists (use --force to overwrite)`;
|
|
206
|
+
if (plan.tags !== undefined) {
|
|
207
|
+
const tagErrors = validateAgentTags(plan.tags, { minCount: AGENT_TAG_MIN_COUNT, maxCount: AGENT_TAG_MAX_COUNT });
|
|
208
|
+
if (tagErrors.length > 0)
|
|
209
|
+
return `Invalid tags: ${tagErrors.join('; ')}`;
|
|
210
|
+
}
|
|
211
|
+
const typeError = validateAgentType(plan.type);
|
|
212
|
+
if (typeError)
|
|
213
|
+
return `Invalid --type: ${typeError}`;
|
|
214
|
+
// 只有显式传入的 --description 才会超限(package.json 的超长值在 resolve 阶段
|
|
215
|
+
// 已被弃用)。必须在这里就拒绝:否则 plan 通过、直到 agentmdPut 上传才失败,
|
|
216
|
+
// 而那处是 warn 级不中断创建,最终留下一张 SDK 兜底名片(--type/--name 全部丢失)。
|
|
217
|
+
const descriptionLength = Array.from(plan.description).length;
|
|
218
|
+
if (descriptionLength > AGENT_DESCRIPTION_MAX_LENGTH) {
|
|
219
|
+
return `Invalid --description: ${descriptionLength} characters exceeds the agent.md limit of ${AGENT_DESCRIPTION_MAX_LENGTH}. Pass a shorter --description.`;
|
|
220
|
+
}
|
|
137
221
|
if (!plan.baseagent)
|
|
138
222
|
return 'No usable baseagent detected. Pass --baseagent or configure a baseagent in agents/defaults.json.';
|
|
139
223
|
if (!BASEAGENT_CANDIDATES.includes(plan.baseagent)) {
|
|
@@ -305,15 +389,25 @@ export async function agentCreateInteractive(opts = {}) {
|
|
|
305
389
|
const defaultName = aid.split('.')[0];
|
|
306
390
|
const agentName = (await ask(`Display name [${defaultName}]: `)).trim() || defaultName;
|
|
307
391
|
const agentDescription = (await ask('Description (optional): ')).trim() || '';
|
|
392
|
+
const tagInput = (await ask('Tags (3-6, comma separated; optional): ')).trim();
|
|
393
|
+
let agentTags;
|
|
394
|
+
if (tagInput) {
|
|
395
|
+
agentTags = [...new Set(tagInput.split(/[,,\s]+/).map(tag => tag.trim()).filter(Boolean))];
|
|
396
|
+
const tagErrors = validateAgentTags(agentTags, { minCount: AGENT_TAG_MIN_COUNT, maxCount: AGENT_TAG_MAX_COUNT });
|
|
397
|
+
if (tagErrors.length > 0)
|
|
398
|
+
return { ok: false, error: `Invalid tags: ${tagErrors.join('; ')}` };
|
|
399
|
+
}
|
|
308
400
|
if (ownRl)
|
|
309
401
|
rl.close();
|
|
310
402
|
saveAgent(withStaticOwner({ $schema_version: CONFIG_SCHEMA_VERSION, aid, enabled: true, lifecycle: 'created', channels: [], projects: { defaultPath: projectPath } }, owner));
|
|
311
403
|
ensureInitialRoleRegistry(aid);
|
|
312
404
|
saveInitialBehavior(aid, baseagent);
|
|
313
405
|
ensureAgentDirSkeleton(aid);
|
|
406
|
+
if (owner)
|
|
407
|
+
await syncOwnerContactsAfterCreate(aid, [owner]);
|
|
314
408
|
let agentmdUploaded = false;
|
|
315
409
|
try {
|
|
316
|
-
const content = buildInitialAgentMd({ aid, name: agentName, description: agentDescription });
|
|
410
|
+
const content = buildInitialAgentMd({ aid, name: agentName, description: agentDescription, tags: agentTags });
|
|
317
411
|
const aunPath = process.env.AUN_HOME || defaultAunPath();
|
|
318
412
|
let lastError;
|
|
319
413
|
for (let attempt = 1; attempt <= 3; attempt++) {
|
|
@@ -366,7 +460,21 @@ export async function agentCreateInteractive(opts = {}) {
|
|
|
366
460
|
ownerBoundAid = await promptAgentOwnerManually(aid);
|
|
367
461
|
}
|
|
368
462
|
}
|
|
369
|
-
|
|
463
|
+
const finalOwnerAid = owner ?? ownerBoundAid;
|
|
464
|
+
const ownerContactSync = await syncOwnerContactsAfterCreate(aid, finalOwnerAid ? [finalOwnerAid] : undefined);
|
|
465
|
+
return {
|
|
466
|
+
ok: true,
|
|
467
|
+
aid,
|
|
468
|
+
configPath: toPosix(path.join(agentDirPath, 'config.json')),
|
|
469
|
+
aidCreated,
|
|
470
|
+
agentmdUploaded,
|
|
471
|
+
hotLoaded,
|
|
472
|
+
hotLoadError,
|
|
473
|
+
ownerBoundAid,
|
|
474
|
+
ownerBindSkipped,
|
|
475
|
+
ownerBindInterrupted,
|
|
476
|
+
...ownerContactSync,
|
|
477
|
+
};
|
|
370
478
|
}
|
|
371
479
|
finally {
|
|
372
480
|
if (ownRl) {
|
|
@@ -411,6 +519,22 @@ export async function agentCreateNonInteractive(opts, runtime = {}) {
|
|
|
411
519
|
const ownerError = validateAgentOwnerOptions(opts);
|
|
412
520
|
if (ownerError)
|
|
413
521
|
return failValidating(ownerError);
|
|
522
|
+
if (opts.tags !== undefined) {
|
|
523
|
+
const tagErrors = validateAgentTags(opts.tags, { minCount: AGENT_TAG_MIN_COUNT, maxCount: AGENT_TAG_MAX_COUNT });
|
|
524
|
+
if (tagErrors.length > 0)
|
|
525
|
+
return failValidating(`Invalid tags: ${tagErrors.join('; ')}`);
|
|
526
|
+
}
|
|
527
|
+
if (opts.type !== undefined) {
|
|
528
|
+
const typeError = validateAgentType(opts.type);
|
|
529
|
+
if (typeError)
|
|
530
|
+
return failValidating(`Invalid --type: ${typeError}`);
|
|
531
|
+
}
|
|
532
|
+
if (opts.description !== undefined) {
|
|
533
|
+
const descriptionLength = Array.from(opts.description).length;
|
|
534
|
+
if (descriptionLength > AGENT_DESCRIPTION_MAX_LENGTH) {
|
|
535
|
+
return failValidating(`Invalid description: ${descriptionLength} characters exceeds the agent.md limit of ${AGENT_DESCRIPTION_MAX_LENGTH}. Pass a shorter --description.`);
|
|
536
|
+
}
|
|
537
|
+
}
|
|
414
538
|
if (!isValidAid(opts.aid))
|
|
415
539
|
return failValidating(`Invalid AID "${opts.aid}": must be a valid multi-level domain (e.g. mybot.agentid.pub)`);
|
|
416
540
|
const agentDirPath = path.join(p.agentsDir, opts.aid);
|
|
@@ -480,11 +604,13 @@ export async function agentCreateNonInteractive(opts, runtime = {}) {
|
|
|
480
604
|
ensureInitialRoleRegistry(opts.aid);
|
|
481
605
|
saveInitialBehavior(opts.aid, baseagent);
|
|
482
606
|
ensureAgentDirSkeleton(opts.aid);
|
|
607
|
+
const ownerContactSync = await syncOwnerContactsAfterCreate(opts.aid, opts.owners ?? (opts.owner ? [opts.owner] : undefined));
|
|
483
608
|
opts.onPhase?.('config_saved', 'done');
|
|
484
609
|
opts.onPhase?.('uploading_agentmd', 'begin');
|
|
485
610
|
let agentmdUploaded = false;
|
|
611
|
+
let agentmdError;
|
|
486
612
|
try {
|
|
487
|
-
const content = buildInitialAgentMd({ aid: opts.aid, name: opts.name, description: opts.description });
|
|
613
|
+
const content = buildInitialAgentMd({ aid: opts.aid, name: opts.name, description: opts.description, type: opts.type, tags: opts.tags });
|
|
488
614
|
const aunPath = process.env.AUN_HOME || defaultAunPath();
|
|
489
615
|
let lastError;
|
|
490
616
|
for (let attempt = 1; attempt <= 3; attempt++) {
|
|
@@ -500,12 +626,15 @@ export async function agentCreateNonInteractive(opts, runtime = {}) {
|
|
|
500
626
|
lastError = e;
|
|
501
627
|
}
|
|
502
628
|
}
|
|
503
|
-
if (!agentmdUploaded)
|
|
504
|
-
|
|
629
|
+
if (!agentmdUploaded) {
|
|
630
|
+
agentmdError = `upload failed: ${lastError?.message || lastError}`;
|
|
631
|
+
opts.onPhase?.('uploading_agentmd', 'warn', agentmdError);
|
|
632
|
+
}
|
|
505
633
|
await new Promise(r => setTimeout(r, 0));
|
|
506
634
|
}
|
|
507
635
|
catch (e) {
|
|
508
|
-
|
|
636
|
+
agentmdError = `generation failed: ${e?.message || e}`;
|
|
637
|
+
opts.onPhase?.('uploading_agentmd', 'warn', agentmdError);
|
|
509
638
|
}
|
|
510
639
|
let hotLoaded = false;
|
|
511
640
|
let hotLoadError;
|
|
@@ -534,5 +663,16 @@ export async function agentCreateNonInteractive(opts, runtime = {}) {
|
|
|
534
663
|
hotLoadError = error instanceof Error ? error.message : String(error);
|
|
535
664
|
opts.onPhase?.('hot_loading', 'warn', hotLoadError);
|
|
536
665
|
}
|
|
537
|
-
return {
|
|
666
|
+
return {
|
|
667
|
+
ok: true,
|
|
668
|
+
aid: opts.aid,
|
|
669
|
+
ownerAid: (opts.owners ?? (opts.owner ? [opts.owner] : undefined))?.[0],
|
|
670
|
+
configPath: toPosix(path.join(agentDirPath, 'config.json')),
|
|
671
|
+
aidCreated,
|
|
672
|
+
agentmdUploaded,
|
|
673
|
+
agentmdError,
|
|
674
|
+
hotLoaded,
|
|
675
|
+
hotLoadError,
|
|
676
|
+
...ownerContactSync,
|
|
677
|
+
};
|
|
538
678
|
}
|
package/dist/cli/agent.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import fs from 'fs';
|
|
2
2
|
import path from 'path';
|
|
3
|
-
import { resolvePaths, agentMdPath as getAgentMdPathFromPaths, aunPath as defaultAunPath } from '../paths.js';
|
|
3
|
+
import { resolvePaths, agentMdPath as getAgentMdPathFromPaths, agentPersonalDir, aunPath as defaultAunPath } from '../paths.js';
|
|
4
4
|
import { loadDefaults, loadAllAgents, loadAgent, saveAgent, ensureAgentDirSkeleton } from '../config-store.js';
|
|
5
5
|
import { initConfigManager } from '../config/config-manager.js';
|
|
6
6
|
import { resolveConfigCommand } from '../config/resolved-config-op.js';
|
|
7
|
-
import {
|
|
7
|
+
import { executeResolvedConfigCommandAsync } from '../config/config-operation-service.js';
|
|
8
8
|
import { ipcQuery, IpcTransportError } from '../ipc.js';
|
|
9
9
|
import { CONFIG_SCHEMA_VERSION } from '../types.js';
|
|
10
10
|
import { ensureInitialRoleRegistry } from '../config/role-service.js';
|
|
@@ -12,6 +12,7 @@ import { agentProjectRootFromDefaults, deriveAgentProjectPath } from '../utils/p
|
|
|
12
12
|
import { isValidChannelName } from '../core/channel-loader.js';
|
|
13
13
|
import { AGENT_DELEGATION_TOKEN_ENV } from '../core/auth/agent-delegation.js';
|
|
14
14
|
import { readTaskRuntimeContextFromEnv } from './task-context.js';
|
|
15
|
+
import { isHClassPath, isLClassPath, isSameOrDescendant } from '../core/permission/protected-paths.js';
|
|
15
16
|
export { agentCreateInteractive, agentCreateNonInteractive, parseAgentOwnerArgs, resolveAgentCreatePlan, validateAgentCreatePlan, validateAgentOwnerOptions, } from './agent-create.js';
|
|
16
17
|
/** Route agent lifecycle commands from a managed task through daemon IPC. */
|
|
17
18
|
export async function runManagedAgentOperation(argv) {
|
|
@@ -182,6 +183,7 @@ export async function agentShow(aid) {
|
|
|
182
183
|
ok: true,
|
|
183
184
|
aid,
|
|
184
185
|
status: agentInfo.status || 'unknown',
|
|
186
|
+
lifecycle: loadAgent(aid)?.lifecycle ?? null,
|
|
185
187
|
identity,
|
|
186
188
|
config: {
|
|
187
189
|
baseagent: agentInfo.baseagent || null,
|
|
@@ -389,12 +391,22 @@ async function runManagedAgentConfig(args, options) {
|
|
|
389
391
|
return { managed: true, error: { ok: false, code: 'DAEMON_UNAVAILABLE', error: 'Unable to connect to the evolcore daemon' } };
|
|
390
392
|
}
|
|
391
393
|
}
|
|
392
|
-
function executeLocalAgentConfig(args) {
|
|
394
|
+
async function executeLocalAgentConfig(args) {
|
|
393
395
|
initConfigManager();
|
|
394
396
|
const resolved = resolveConfigCommand(['config', ...args]);
|
|
395
397
|
if (!resolved.ok)
|
|
396
398
|
return { ok: false, code: resolved.code, error: resolved.reason };
|
|
397
|
-
return
|
|
399
|
+
return executeResolvedConfigCommandAsync(resolved.command, {
|
|
400
|
+
role: 'owner',
|
|
401
|
+
roleContext: {
|
|
402
|
+
self: resolved.command.self || '',
|
|
403
|
+
actorRole: 'owner',
|
|
404
|
+
actorRoleSource: 'local-cli',
|
|
405
|
+
isDaemonOwner: false,
|
|
406
|
+
fromControlChannel: false,
|
|
407
|
+
aunAvailable: true,
|
|
408
|
+
},
|
|
409
|
+
});
|
|
398
410
|
}
|
|
399
411
|
export async function agentGet(aid, key, options = {}) {
|
|
400
412
|
const managed = await runManagedAgentConfig(['get', key, '--self', aid], options);
|
|
@@ -411,7 +423,7 @@ export async function agentGet(aid, key, options = {}) {
|
|
|
411
423
|
return { ok: false, error: `Agent "${aid}" not found` };
|
|
412
424
|
}
|
|
413
425
|
try {
|
|
414
|
-
const result = executeLocalAgentConfig(['get', key, '--self', aid]);
|
|
426
|
+
const result = await executeLocalAgentConfig(['get', key, '--self', aid]);
|
|
415
427
|
return result.ok && result.subcommand === 'get'
|
|
416
428
|
? { ok: true, aid, key, value: result.value }
|
|
417
429
|
: { ok: false, code: result.ok ? 'UNEXPECTED_RESULT' : result.code, error: result.ok ? 'config get returned an unexpected result' : result.error };
|
|
@@ -437,7 +449,7 @@ export async function agentSet(aid, key, rawValue, options = {}) {
|
|
|
437
449
|
}
|
|
438
450
|
let value;
|
|
439
451
|
try {
|
|
440
|
-
const result = executeLocalAgentConfig(['set', key, rawValue, '--self', aid]);
|
|
452
|
+
const result = await executeLocalAgentConfig(['set', key, rawValue, '--self', aid]);
|
|
441
453
|
if (!result.ok)
|
|
442
454
|
return { ok: false, code: result.code, error: result.error };
|
|
443
455
|
if (result.subcommand !== 'set')
|
|
@@ -456,6 +468,107 @@ export async function agentSet(aid, key, rawValue, options = {}) {
|
|
|
456
468
|
catch { }
|
|
457
469
|
return { ok: true, aid, key, value, reloaded };
|
|
458
470
|
}
|
|
471
|
+
export const AGENT_PERSONA_MAX_BYTES = 64 * 1024;
|
|
472
|
+
function validatePersonaContent(content) {
|
|
473
|
+
if (content.includes('\0'))
|
|
474
|
+
return 'persona 只能包含文本内容';
|
|
475
|
+
if (Buffer.byteLength(content, 'utf8') > AGENT_PERSONA_MAX_BYTES) {
|
|
476
|
+
return `persona 不能超过 ${AGENT_PERSONA_MAX_BYTES} bytes`;
|
|
477
|
+
}
|
|
478
|
+
return null;
|
|
479
|
+
}
|
|
480
|
+
function readPersonaSource(file) {
|
|
481
|
+
const resolved = path.resolve(file);
|
|
482
|
+
if (!/\.(?:md|txt)$/i.test(resolved)) {
|
|
483
|
+
return { ok: false, code: 'INVALID_ARGS', error: '--file 只接受 .md 或 .txt 文本文件' };
|
|
484
|
+
}
|
|
485
|
+
try {
|
|
486
|
+
const stat = fs.statSync(resolved);
|
|
487
|
+
if (!stat.isFile())
|
|
488
|
+
return { ok: false, code: 'INVALID_ARGS', error: `--file 必须指向普通文件: ${resolved}` };
|
|
489
|
+
const content = fs.readFileSync(resolved, 'utf8');
|
|
490
|
+
const error = validatePersonaContent(content);
|
|
491
|
+
return error ? { ok: false, code: 'INVALID_ARGS', error } : { ok: true, content };
|
|
492
|
+
}
|
|
493
|
+
catch (e) {
|
|
494
|
+
return { ok: false, code: 'READ_FAILED', error: `无法读取 --file: ${e?.message || e}` };
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
/** Read or replace the current Agent's personal/persona.md. */
|
|
498
|
+
export async function agentPersona(aid, options = {}) {
|
|
499
|
+
if (options.text !== undefined && options.file !== undefined)
|
|
500
|
+
return { ok: false, code: 'INVALID_ARGS', error: '--text 与 --file 不能同时使用' };
|
|
501
|
+
const managedSession = !!process.env.EVOLCORE_SESSION_ID && !options.trustedLocal;
|
|
502
|
+
let text = options.text;
|
|
503
|
+
if (options.file !== undefined) {
|
|
504
|
+
if (managedSession) {
|
|
505
|
+
const resolved = path.resolve(options.file);
|
|
506
|
+
const projectRoot = path.resolve(process.cwd());
|
|
507
|
+
const tempRoot = process.env.TMPDIR?.trim() && path.isAbsolute(process.env.TMPDIR)
|
|
508
|
+
? path.resolve(process.env.TMPDIR)
|
|
509
|
+
: undefined;
|
|
510
|
+
const allowed = isSameOrDescendant(resolved, projectRoot)
|
|
511
|
+
|| (!!tempRoot && isSameOrDescendant(resolved, tempRoot));
|
|
512
|
+
if (!allowed || isHClassPath(resolved) || isLClassPath(resolved)) {
|
|
513
|
+
return { ok: false, code: 'INVALID_ARGS', error: '--file 必须位于当前项目或会话 $TMPDIR,且不能读取 EvolCore 受保护路径' };
|
|
514
|
+
}
|
|
515
|
+
try {
|
|
516
|
+
const stat = fs.lstatSync(resolved);
|
|
517
|
+
if (stat.isSymbolicLink() || !stat.isFile()) {
|
|
518
|
+
return { ok: false, code: 'INVALID_ARGS', error: '--file 必须指向当前项目或会话 $TMPDIR 内的普通文件' };
|
|
519
|
+
}
|
|
520
|
+
const realPath = fs.realpathSync(resolved);
|
|
521
|
+
const realProjectRoot = fs.realpathSync(projectRoot);
|
|
522
|
+
const realTempRoot = tempRoot ? fs.realpathSync(tempRoot) : undefined;
|
|
523
|
+
const realAllowed = isSameOrDescendant(realPath, realProjectRoot)
|
|
524
|
+
|| (!!realTempRoot && isSameOrDescendant(realPath, realTempRoot));
|
|
525
|
+
if (!realAllowed || isHClassPath(realPath) || isLClassPath(realPath)) {
|
|
526
|
+
return { ok: false, code: 'INVALID_ARGS', error: '--file 解析后越出允许目录或命中 EvolCore 受保护路径' };
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
catch (e) {
|
|
530
|
+
return { ok: false, code: 'READ_FAILED', error: `无法校验 --file: ${e?.message || e}` };
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
const source = readPersonaSource(options.file);
|
|
534
|
+
if (!source.ok)
|
|
535
|
+
return source;
|
|
536
|
+
text = source.content;
|
|
537
|
+
}
|
|
538
|
+
if (text !== undefined) {
|
|
539
|
+
const error = validatePersonaContent(text);
|
|
540
|
+
if (error)
|
|
541
|
+
return { ok: false, code: 'INVALID_ARGS', error };
|
|
542
|
+
}
|
|
543
|
+
const managedArgs = options.text !== undefined
|
|
544
|
+
? ['persona', '--text', text]
|
|
545
|
+
: options.file !== undefined
|
|
546
|
+
? ['persona', '--text', text]
|
|
547
|
+
: ['persona', 'show'];
|
|
548
|
+
if (managedSession) {
|
|
549
|
+
const managed = await runManagedAgentOperation(managedArgs);
|
|
550
|
+
if (!managed.ok)
|
|
551
|
+
return managed;
|
|
552
|
+
return { ok: true, aid, content: managed.content ?? null, ...(managed.path ? { path: managed.path } : {}), ...(managed.saved ? { saved: true } : {}) };
|
|
553
|
+
}
|
|
554
|
+
const personaPath = path.join(agentPersonalDir(aid), 'persona.md');
|
|
555
|
+
if (text === undefined) {
|
|
556
|
+
try {
|
|
557
|
+
return { ok: true, aid, content: fs.existsSync(personaPath) ? fs.readFileSync(personaPath, 'utf8') : null, path: personaPath };
|
|
558
|
+
}
|
|
559
|
+
catch (e) {
|
|
560
|
+
return { ok: false, code: 'READ_FAILED', error: e?.message || String(e) };
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
try {
|
|
564
|
+
fs.mkdirSync(path.dirname(personaPath), { recursive: true });
|
|
565
|
+
fs.writeFileSync(personaPath, text, 'utf8');
|
|
566
|
+
return { ok: true, aid, content: text, path: personaPath, saved: true };
|
|
567
|
+
}
|
|
568
|
+
catch (e) {
|
|
569
|
+
return { ok: false, code: 'WRITE_FAILED', error: e?.message || String(e) };
|
|
570
|
+
}
|
|
571
|
+
}
|
|
459
572
|
/**
|
|
460
573
|
* Add or overwrite a channel instance on a per-agent config.
|
|
461
574
|
*
|