evolcore 0.0.24 → 0.0.25
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 +578 -561
- package/LICENSE +21 -21
- package/README.md +455 -455
- package/bin/codex-managed-acl.ps1 +160 -160
- package/bin/codex-managed-hook.mjs +208 -208
- package/bin/ec-safe-output.js +327 -327
- package/bin/ec.js +29 -29
- package/bin/install-codex-managed-hooks.mjs +555 -555
- package/dist/agents/claude-runner.js +4 -1
- package/dist/agents/codex-runner.js +7 -4
- package/dist/agents/ecagent-runner.js +14 -13
- package/dist/agents/gemini-runner.js +22 -2
- package/dist/aun/aid/agentmd.js +56 -4
- package/dist/aun/aid/index.js +1 -0
- package/dist/aun/aid/managed-operation.js +53 -1
- package/dist/aun/storage/client.js +4 -1
- package/dist/aun/storage/index.js +1 -1
- package/dist/cli/agent-command.js +186 -87
- package/dist/cli/agent-create.js +42 -4
- package/dist/cli/agent.js +103 -1
- package/dist/cli/aun-commands.js +360 -294
- package/dist/cli/bench.js +20 -20
- package/dist/cli/config.js +20 -20
- package/dist/cli/contact.js +10 -10
- package/dist/cli/ctl-command.js +22 -22
- package/dist/cli/daemon-commands.js +15 -15
- package/dist/cli/fs-command.js +67 -67
- package/dist/cli/handoff-command.js +7 -7
- package/dist/cli/index.js +77 -77
- package/dist/cli/link-rules.js +21 -21
- package/dist/cli/menu-client.js +11 -11
- package/dist/cli/net-check.js +22 -22
- package/dist/cli/queue-command.js +29 -29
- package/dist/cli/response.js +30 -30
- package/dist/cli/restart-monitor.js +30 -30
- package/dist/cli/stats.js +51 -51
- package/dist/cli/trigger-command.js +56 -56
- package/dist/config/builtin-roles.js +1 -0
- package/dist/config/config-manager.js +15 -0
- package/dist/config/config-operation-service.js +20 -3
- package/dist/config/resolved-config-op.js +39 -1
- package/dist/core/auth/operation-authorizer.js +62 -4
- package/dist/core/auth/operation-catalog.js +16 -0
- 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 +91 -16
- package/dist/core/command/menu-catalog.js +1 -0
- package/dist/core/command/menu-handler.js +41 -5
- package/dist/core/command/menu-protocol.js +1 -0
- package/dist/core/command/slash-handler.js +9 -9
- package/dist/core/event-catalog.js +5 -0
- package/dist/core/evolagent.js +3 -3
- package/dist/core/message/response-engine.js +12 -6
- package/dist/core/permission/tool-policy.js +45 -6
- package/dist/index.js +209 -162
- package/dist/stats/db.js +159 -159
- package/dist/stats/eck-vars.js +9 -9
- package/dist/stats/query.js +139 -139
- package/dist/stats/role-budget.js +6 -6
- package/dist/stats/writer.js +51 -51
- package/dist/utils/avatar-upload.js +282 -23
- package/dist/utils/error-dict.json +160 -160
- package/dist/utils/macos-autostart.js +22 -22
- package/dist/utils/model-prices.jsonl +23 -23
- package/dist/utils/welcome.js +22 -22
- package/ecagent/LICENSE +21 -21
- package/ecagent/NOTICE +11 -11
- package/ecagent/README.md +61 -61
- package/ecagent/dist/harness/compaction/branch-summarization.js +30 -30
- package/ecagent/dist/harness/compaction/compaction.js +83 -83
- package/ecagent/dist/harness/messages.js +7 -7
- package/kits/docs/GUIDE.md +20 -20
- package/kits/docs/INDEX.md +66 -66
- package/kits/docs/aun/CHEATSHEET.md +19 -19
- package/kits/docs/aun/SYNC_PROTOCOL.md +15 -15
- package/kits/docs/channels/aun.md +56 -56
- package/kits/docs/channels/feishu.md +56 -56
- package/kits/docs/context-assembly.md +365 -364
- package/kits/docs/eck_templates/GUIDE.template.md +22 -22
- package/kits/docs/eck_templates/INDEX.template.md +28 -28
- package/kits/docs/eck_templates/path-registry.template.md +33 -33
- package/kits/docs/eck_templates/runtime.template.md +19 -19
- package/kits/docs/evolcore/INDEX.md +71 -69
- package/kits/docs/evolcore/agent-create.md +170 -156
- package/kits/docs/evolcore/agent.md +83 -73
- package/kits/docs/evolcore/aid.md +64 -62
- package/kits/docs/evolcore/codex-windows-sandbox.md +146 -0
- package/kits/docs/evolcore/config.md +195 -195
- package/kits/docs/evolcore/contact.md +68 -68
- package/kits/docs/evolcore/ctl.md +46 -46
- package/kits/docs/evolcore/event.md +216 -216
- package/kits/docs/evolcore/fs-architecture.md +1215 -1215
- package/kits/docs/evolcore/fs.md +110 -110
- package/kits/docs/evolcore/group-collaboration.md +243 -243
- package/kits/docs/evolcore/group-fs.md +17 -17
- package/kits/docs/evolcore/group-rules.md +251 -251
- package/kits/docs/evolcore/group.md +152 -152
- package/kits/docs/evolcore/menu-client.md +34 -34
- package/kits/docs/evolcore/model.md +50 -50
- package/kits/docs/evolcore/msg.md +165 -165
- package/kits/docs/evolcore/response.md +75 -75
- package/kits/docs/evolcore/rpc.md +37 -37
- package/kits/docs/evolcore/self-summary.md +29 -29
- package/kits/docs/evolcore/stats.md +83 -83
- package/kits/docs/evolcore/storage.md +50 -50
- package/kits/docs/evolcore/trigger.md +575 -575
- package/kits/docs/identity/AID_PROFILE_SPEC.md +26 -26
- package/kits/docs/identity/PATH_OPS.md +16 -16
- package/kits/docs/identity/ROLE_DETAIL.md +25 -25
- package/kits/docs/identity/identity-tools.md +26 -26
- package/kits/docs/path-registry.md +62 -62
- package/kits/docs/prompt-loading-architecture.md +270 -269
- package/kits/docs/venues/aun-group.md +45 -45
- package/kits/docs/venues/aun-private.md +10 -10
- package/kits/docs/venues/client-desktop.md +10 -10
- package/kits/docs/venues/client-mobile.md +10 -10
- package/kits/docs/venues/feishu-group.md +13 -13
- package/kits/docs/venues/feishu-private.md +9 -9
- package/kits/docs/venues/group.md +25 -25
- package/kits/docs/venues/private.md +10 -10
- package/kits/eck_manifest.auxiliary.json +43 -43
- package/kits/eck_manifest.json +195 -183
- package/kits/eck_message_manifest.json +74 -74
- package/kits/migrations/README-role-config-v4.md +32 -32
- package/kits/migrations/migrate-contact-book-v2.mjs +748 -748
- package/kits/migrations/migrate-role-config-v4.mjs +614 -614
- package/kits/migrations/migrate-role-config-v5.mjs +365 -365
- package/kits/migrations/rename-config-file.mjs +99 -99
- package/kits/rules/01-overview.md +162 -162
- package/kits/rules/02-navigation.md +96 -96
- package/kits/rules/03-identity.md +39 -37
- package/kits/rules/04-relation.md +75 -75
- package/kits/rules/05-venue.md +60 -60
- package/kits/rules/06-channel.md +63 -63
- package/kits/schemas/_meta.json +53 -53
- package/kits/schemas/agent-config.schema.1.json +177 -177
- package/kits/schemas/agent-config.schema.10.json +395 -395
- package/kits/schemas/agent-config.schema.11.json +421 -421
- package/kits/schemas/agent-config.schema.12.json +427 -427
- package/kits/schemas/agent-config.schema.2.json +239 -239
- package/kits/schemas/agent-config.schema.3.json +119 -119
- package/kits/schemas/agent-config.schema.4.json +208 -208
- package/kits/schemas/agent-config.schema.5.json +326 -326
- package/kits/schemas/agent-config.schema.6.json +322 -322
- package/kits/schemas/agent-config.schema.7.json +303 -303
- package/kits/schemas/agent-config.schema.8.json +304 -304
- package/kits/schemas/agent-config.schema.9.json +364 -364
- package/kits/schemas/contact-book.schema.1.json +36 -36
- package/kits/schemas/contact-book.schema.2.json +43 -43
- package/kits/schemas/contact-book.schema.3.json +67 -67
- package/kits/schemas/daemon.schema.1.json +91 -91
- package/kits/schemas/daemon.schema.2.json +101 -101
- package/kits/schemas/daemon.schema.3.json +123 -123
- package/kits/schemas/daemon.schema.4.json +136 -136
- package/kits/schemas/daemon.schema.5.json +135 -135
- package/kits/schemas/daemon.schema.6.json +131 -131
- package/kits/schemas/defaults.schema.1.json +81 -81
- package/kits/schemas/defaults.schema.2.json +85 -85
- package/kits/schemas/defaults.schema.3.json +73 -73
- package/kits/schemas/defaults.schema.4.json +107 -107
- package/kits/schemas/defaults.schema.5.json +119 -119
- package/kits/schemas/menu-exec-schema-commands.md +208 -208
- package/kits/schemas/migrations/README.md +30 -30
- package/kits/schemas/relation-config.schema.1.json +158 -158
- package/kits/schemas/relation-config.schema.2.json +73 -73
- package/kits/schemas/relation-config.schema.3.json +50 -50
- package/kits/schemas/relation-config.schema.4.json +47 -47
- package/kits/schemas/relation-config.schema.5.json +47 -47
- package/kits/schemas/relation-config.schema.6.json +46 -46
- package/kits/schemas/relation-config.schema.7.json +59 -59
- package/kits/schemas/relation-config.schema.8.json +103 -103
- package/kits/schemas/role-config.schema.1.json +201 -201
- package/kits/schemas/role-registry.schema.1.json +35 -35
- package/kits/schemas/single-session.schema.1.json +31 -31
- package/kits/schemas/single-session.schema.2.json +57 -57
- package/kits/schemas/single-session.schema.3.json +32 -32
- package/kits/templates/bootstrap-welcome.md +17 -17
- package/kits/templates/message-fragments/handoff-context-to-target.md +9 -9
- package/kits/templates/message-fragments/handoff-request-to-target.md +19 -19
- package/kits/templates/message-fragments/handoff-response-to-origin.md +9 -9
- package/kits/templates/message-fragments/inject-default.md +2 -2
- package/kits/templates/message-fragments/item.md +2 -2
- package/kits/templates/roles/admin.json +96 -84
- package/kits/templates/roles/member.json +97 -96
- package/kits/templates/roles/owner.json +9 -9
- package/kits/templates/roles/visitor.json +86 -85
- package/kits/templates/system-fragments/baseagent.md +14 -14
- package/kits/templates/system-fragments/bootstrap.md +38 -29
- package/kits/templates/system-fragments/channel.md +64 -64
- package/kits/templates/system-fragments/commands.md +28 -28
- package/kits/templates/system-fragments/identity.md +13 -13
- package/kits/templates/system-fragments/relation.md +19 -19
- package/kits/templates/system-fragments/session.md +59 -59
- package/kits/templates/system-fragments/venue.md +31 -31
- package/package.json +98 -98
- package/skills/eclink/SKILL.md +0 -84
- package/skills/eclink/agents/openai.yaml +0 -4
|
@@ -192,7 +192,8 @@ function authorizeCommandInternal(ctx, resolvedConfigCommand) {
|
|
|
192
192
|
};
|
|
193
193
|
}
|
|
194
194
|
// Any authenticated role may inspect the current Agent through the daemon's
|
|
195
|
-
// redacted view.
|
|
195
|
+
// redacted view. Management roles may inspect another Agent's redacted view;
|
|
196
|
+
// mutation and reload remain target-scoped below.
|
|
196
197
|
if (operation === 'agent.show' && isCurrentAgentTarget(ctx)) {
|
|
197
198
|
return {
|
|
198
199
|
allow: true,
|
|
@@ -203,6 +204,17 @@ function authorizeCommandInternal(ctx, resolvedConfigCommand) {
|
|
|
203
204
|
matchedRule: 'self-agent-read',
|
|
204
205
|
};
|
|
205
206
|
}
|
|
207
|
+
if (operation === 'agent.persona' && isCurrentAgentTarget(ctx)
|
|
208
|
+
&& (role === 'owner' || hasDaemonOwnerRole(ctx))) {
|
|
209
|
+
return {
|
|
210
|
+
allow: true,
|
|
211
|
+
operation,
|
|
212
|
+
scope: intent.scope,
|
|
213
|
+
role,
|
|
214
|
+
dangerous: opMeta.dangerous,
|
|
215
|
+
matchedRule: role === 'owner' ? 'agent-owner-persona' : 'daemon-owner-persona',
|
|
216
|
+
};
|
|
217
|
+
}
|
|
206
218
|
if (resolvedConfigCommand) {
|
|
207
219
|
const targetCheck = checkResolvedConfigTarget(ctx, resolvedConfigCommand);
|
|
208
220
|
if (!targetCheck.ok) {
|
|
@@ -215,7 +227,7 @@ function authorizeCommandInternal(ctx, resolvedConfigCommand) {
|
|
|
215
227
|
if (!isManagementRole(role) && intent.scope !== 'relation') {
|
|
216
228
|
return denyDecision(ctx, 'SCOPE_MISMATCH', `Role ${role} may only access relation-scoped config`, operation, intent.scope, opMeta.dangerous);
|
|
217
229
|
}
|
|
218
|
-
if (role === 'owner') {
|
|
230
|
+
if (role === 'owner' && isCurrentAgentTarget(ctx)) {
|
|
219
231
|
return {
|
|
220
232
|
allow: true,
|
|
221
233
|
operation,
|
|
@@ -225,6 +237,22 @@ function authorizeCommandInternal(ctx, resolvedConfigCommand) {
|
|
|
225
237
|
matchedRule: 'agent-owner',
|
|
226
238
|
};
|
|
227
239
|
}
|
|
240
|
+
// The daemon control identity is allowed to inspect any Agent config,
|
|
241
|
+
// including sensitive fields. This is intentionally handled before
|
|
242
|
+
// role-definition matching because daemon-owner subjects do not need an
|
|
243
|
+
// Agent relation role to read the daemon's local configuration store.
|
|
244
|
+
if (resolvedConfigCommand.kind === 'field'
|
|
245
|
+
&& resolvedConfigCommand.subcommand === 'get'
|
|
246
|
+
&& hasDaemonOwnerRole(ctx)) {
|
|
247
|
+
return {
|
|
248
|
+
allow: true,
|
|
249
|
+
operation,
|
|
250
|
+
scope: intent.scope,
|
|
251
|
+
role: 'daemon-owner',
|
|
252
|
+
dangerous: opMeta.dangerous,
|
|
253
|
+
matchedRule: 'daemon-owner-config-read',
|
|
254
|
+
};
|
|
255
|
+
}
|
|
228
256
|
}
|
|
229
257
|
const roleDef = getRoleDefinition(role, ctx.selfAid);
|
|
230
258
|
if (!roleDef) {
|
|
@@ -357,10 +385,25 @@ function requiresDaemonOwner(ctx, resolvedConfigCommand) {
|
|
|
357
385
|
|| resolvedConfigCommand.configScope === 'process'
|
|
358
386
|
|| resolvedConfigCommand.configScope === 'defaults'))
|
|
359
387
|
return true;
|
|
388
|
+
// Sensitive reads of another Agent's config are daemon-scoped. Agent
|
|
389
|
+
// owner/admin roles may inspect safe fields cross-Agent, but only the
|
|
390
|
+
// daemon control identity may access config.read targets that are not the
|
|
391
|
+
// current Agent.
|
|
392
|
+
if (operation === 'config.read'
|
|
393
|
+
&& resolvedConfigCommand?.kind === 'field'
|
|
394
|
+
&& resolvedConfigCommand.configScope === 'agent'
|
|
395
|
+
&& !isCurrentAgentTarget(ctx)) {
|
|
396
|
+
return true;
|
|
397
|
+
}
|
|
360
398
|
if (operation === 'agent.reload' && hasDaemonOwnerRole(ctx)) {
|
|
361
399
|
return true;
|
|
362
400
|
}
|
|
363
|
-
|
|
401
|
+
// Avatar publication is an Agent-scoped mutation for Agent channels, but
|
|
402
|
+
// the trusted control plane may publish it for a selected Agent target.
|
|
403
|
+
if (operation === 'ec.aid.avatar' && hasDaemonOwnerRole(ctx)) {
|
|
404
|
+
return true;
|
|
405
|
+
}
|
|
406
|
+
if (operation === 'agent.reload') {
|
|
364
407
|
return !isCurrentAgentTarget(ctx);
|
|
365
408
|
}
|
|
366
409
|
if (operation.startsWith('trigger.')) {
|
|
@@ -529,6 +572,12 @@ function checkConfigFieldPolicy(ctx, policy, resolvedConfigOp) {
|
|
|
529
572
|
return { ok: false, reason: 'Resolved config operation is required' };
|
|
530
573
|
const field = resolvedConfigOp.field;
|
|
531
574
|
if (!resolvedConfigOp.route || (resolvedConfigOp.fieldRule.class !== 'safe-scalar' && resolvedConfigOp.fieldRule.class !== 'safe-readonly-object')) {
|
|
575
|
+
if (policy === 'behavior-read'
|
|
576
|
+
&& !isCurrentAgentTarget(ctx)
|
|
577
|
+
&& hasDaemonOwnerRole(ctx)
|
|
578
|
+
&& resolvedConfigOp.route) {
|
|
579
|
+
return { ok: true };
|
|
580
|
+
}
|
|
532
581
|
return { ok: false, reason: `Config field ${field} is not available to user roles` };
|
|
533
582
|
}
|
|
534
583
|
if (policy === 'role-overridable-write'
|
|
@@ -541,8 +590,12 @@ function checkConfigFieldPolicy(ctx, policy, resolvedConfigOp) {
|
|
|
541
590
|
const fieldPermission = roleDef
|
|
542
591
|
? resolveRoleFieldPermission(roleDef.permissions || {}, field)
|
|
543
592
|
: undefined;
|
|
544
|
-
if (policy === 'behavior-read')
|
|
593
|
+
if (policy === 'behavior-read') {
|
|
594
|
+
// Only the daemon control identity may read sensitive config fields from
|
|
595
|
+
// another Agent. Agent owners retain unrestricted reads for their own
|
|
596
|
+
// target via the self-target fast path above.
|
|
545
597
|
return { ok: true };
|
|
598
|
+
}
|
|
546
599
|
if (fieldPermission && !fieldPermission.allowOverride) {
|
|
547
600
|
return { ok: false, reason: `Role ${ctx.role} cannot override ${field}` };
|
|
548
601
|
}
|
|
@@ -579,6 +632,11 @@ function checkResolvedConfigTarget(ctx, command) {
|
|
|
579
632
|
return { ok: true };
|
|
580
633
|
}
|
|
581
634
|
if (command.configScope === 'agent') {
|
|
635
|
+
// Management roles may read another Agent's non-sensitive config fields;
|
|
636
|
+
// sensitive fields are rejected by the field policy. Writes stay self-only.
|
|
637
|
+
if (command.subcommand === 'get' && (isManagementRole(ctx.role) || hasDaemonOwnerRole(ctx))) {
|
|
638
|
+
return { ok: true };
|
|
639
|
+
}
|
|
582
640
|
if (!ctx.selfAid || !command.self || command.self !== ctx.selfAid) {
|
|
583
641
|
return { ok: false, reason: 'Only the current agent can be targeted' };
|
|
584
642
|
}
|
|
@@ -601,6 +601,14 @@ const OPERATIONS = [
|
|
|
601
601
|
description: '获取 agent 配置(含凭证)',
|
|
602
602
|
sources: ['menu', 'menu.cli', 'control'],
|
|
603
603
|
},
|
|
604
|
+
{
|
|
605
|
+
id: 'agent.persona',
|
|
606
|
+
category: 'write-agent',
|
|
607
|
+
dangerous: false,
|
|
608
|
+
defaultScopes: ['agent'],
|
|
609
|
+
description: '读取或更新当前 Agent 的 persona.md',
|
|
610
|
+
sources: ['agent-tool', 'control'],
|
|
611
|
+
},
|
|
604
612
|
{
|
|
605
613
|
id: 'agent.create',
|
|
606
614
|
category: 'process',
|
|
@@ -944,6 +952,14 @@ const OPERATIONS = [
|
|
|
944
952
|
description: 'Sign and publish the current Agent local agent.md',
|
|
945
953
|
sources: ['agent-tool'],
|
|
946
954
|
},
|
|
955
|
+
{
|
|
956
|
+
id: 'ec.aid.avatar',
|
|
957
|
+
category: 'write-agent',
|
|
958
|
+
dangerous: true,
|
|
959
|
+
defaultScopes: ['agent'],
|
|
960
|
+
description: 'Upload and publish the current Agent avatar',
|
|
961
|
+
sources: ['agent-tool', 'menu', 'menu.cli'],
|
|
962
|
+
},
|
|
947
963
|
{
|
|
948
964
|
id: 'ec.msg.send',
|
|
949
965
|
category: 'write-own',
|
|
@@ -6,11 +6,11 @@ import path from 'path';
|
|
|
6
6
|
import { ConfigTarget, ensureFile as cfgEnsure, read as cfgRead, write as cfgWrite, } from '../../config/config-manager.js';
|
|
7
7
|
import { CreateStatusWriter, readCreateStatus } from '../message/create-status.js';
|
|
8
8
|
import { agentProjectRootFromDefaults, deriveAgentProjectPath } from '../../utils/project-path.js';
|
|
9
|
-
import { uploadAvatar } from '../../utils/avatar-upload.js';
|
|
9
|
+
import { uploadAvatar, uploadAvatarUrl } from '../../utils/avatar-upload.js';
|
|
10
10
|
import { agentmdGet, agentmdPut, updateAgentMdFrontmatterName } from '../../aun/aid/agentmd.js';
|
|
11
11
|
import { isValidAid } from '../../aun/aid/validation.js';
|
|
12
12
|
import { isSupportedBaseagent } from '../../agents/baseagent.js';
|
|
13
|
-
const SUPPORTED_AGENT_PATCH_FIELDS = new Set(['aid', 'name', '
|
|
13
|
+
const SUPPORTED_AGENT_PATCH_FIELDS = new Set(['aid', 'name', 'active_baseagent', 'baseagents', 'projects', 'owners', 'chatmode', 'channels', 'channelOwners']);
|
|
14
14
|
const HIDDEN_VALUE = '[hidden]';
|
|
15
15
|
const SENSITIVE_CONFIG_KEYS = new Set([
|
|
16
16
|
'apikey',
|
|
@@ -48,6 +48,42 @@ function classifyAgentMdError(error) {
|
|
|
48
48
|
return 'INVALID_ARGS';
|
|
49
49
|
return 'UPLOAD_FAILED';
|
|
50
50
|
}
|
|
51
|
+
/** Canonical avatar mutation for the `aid.avatar` menu action. */
|
|
52
|
+
export async function execAidAvatar(args) {
|
|
53
|
+
const a = args ?? {};
|
|
54
|
+
const aid = typeof a.aid === 'string' ? a.aid.trim().replace(/^@/, '') : '';
|
|
55
|
+
if (!aid)
|
|
56
|
+
return { error: '缺少 aid', code: 'INVALID_ARGS' };
|
|
57
|
+
if (!isValidAid(aid))
|
|
58
|
+
return { error: `无效 aid: ${aid}`, code: 'INVALID_ARGS' };
|
|
59
|
+
const hasData = a.data !== undefined;
|
|
60
|
+
const hasUrl = a.url !== undefined;
|
|
61
|
+
if ((hasData ? 1 : 0) + (hasUrl ? 1 : 0) > 1) {
|
|
62
|
+
return { error: '头像数据源必须在 data 与 url 中二选一', code: 'INVALID_ARGS' };
|
|
63
|
+
}
|
|
64
|
+
if (!hasData && !hasUrl) {
|
|
65
|
+
return { error: '缺少头像数据源:需要 data 或 url', code: 'INVALID_ARGS' };
|
|
66
|
+
}
|
|
67
|
+
const result = hasUrl
|
|
68
|
+
? await uploadAvatarUrl(aid, a.url, { aunPath: resolveRoot() })
|
|
69
|
+
: await uploadAvatar(aid, a.data, { aunPath: resolveRoot() });
|
|
70
|
+
if (!result.ok) {
|
|
71
|
+
return { error: `头像上传失败: ${result.error}`, code: result.code ?? 'UPLOAD_FAILED' };
|
|
72
|
+
}
|
|
73
|
+
return {
|
|
74
|
+
data: {
|
|
75
|
+
aid,
|
|
76
|
+
avatar: result.publicUrl,
|
|
77
|
+
objectKey: result.objectKey,
|
|
78
|
+
sha256: result.sha256,
|
|
79
|
+
mime: result.mime,
|
|
80
|
+
width: result.width,
|
|
81
|
+
height: result.height,
|
|
82
|
+
aspectRatio: result.aspectRatio,
|
|
83
|
+
saved: true,
|
|
84
|
+
},
|
|
85
|
+
};
|
|
86
|
+
}
|
|
51
87
|
async function updateAgentMdName(aid, value) {
|
|
52
88
|
const name = typeof value === 'string' ? value.trim() : '';
|
|
53
89
|
if (!name)
|
|
@@ -299,6 +335,7 @@ async function runCreateInBackground(opts, w, service) {
|
|
|
299
335
|
aid: opts.aid, name: opts.name, baseagent: opts.baseagent,
|
|
300
336
|
project: opts.project, owner: opts.owner,
|
|
301
337
|
description: opts.description,
|
|
338
|
+
tags: opts.tags,
|
|
302
339
|
onPhase: (phase, state, detail) => {
|
|
303
340
|
if (state === 'begin') {
|
|
304
341
|
curPhase = phase;
|
|
@@ -398,6 +435,7 @@ export async function execAgentAction(action, args, peerId, eventBus, service) {
|
|
|
398
435
|
const description = typeof a.description === 'string' && a.description.trim()
|
|
399
436
|
? a.description.trim()
|
|
400
437
|
: undefined;
|
|
438
|
+
const tags = Array.isArray(a.tags) ? a.tags : undefined;
|
|
401
439
|
// D3: accepted=true 必须意味着后续 query 能查到该 AID 的创建状态。
|
|
402
440
|
let progressWriter;
|
|
403
441
|
try {
|
|
@@ -410,7 +448,7 @@ export async function execAgentAction(action, args, peerId, eventBus, service) {
|
|
|
410
448
|
void runCreateInBackground({
|
|
411
449
|
aid: a.aid, name: a.name, baseagent: a.baseagent,
|
|
412
450
|
project: a.project, owner,
|
|
413
|
-
model, effort, description, chatmode: a.chatmode,
|
|
451
|
+
model, effort, description, tags, chatmode: a.chatmode,
|
|
414
452
|
eventBus,
|
|
415
453
|
}, progressWriter, service).catch(e => logger.error(`[agent-control] runCreateInBackground unhandled ${a.aid}: ${e?.message || e}`));
|
|
416
454
|
return { data: { accepted: true, aid: a.aid } };
|
|
@@ -461,14 +499,6 @@ export async function execAgentAction(action, args, peerId, eventBus, service) {
|
|
|
461
499
|
if (action === 'update') {
|
|
462
500
|
const result = await execAgentUpdate(a);
|
|
463
501
|
if (!('error' in result)) {
|
|
464
|
-
if (result.data?.avatar) {
|
|
465
|
-
eventBus?.publish({
|
|
466
|
-
type: 'agent:avatar_updated',
|
|
467
|
-
aid: a.aid,
|
|
468
|
-
avatar: result.data.avatar,
|
|
469
|
-
timestamp: Date.now(),
|
|
470
|
-
});
|
|
471
|
-
}
|
|
472
502
|
eventBus?.publish({
|
|
473
503
|
type: 'agent:updated',
|
|
474
504
|
aid: a.aid,
|
|
@@ -501,12 +531,6 @@ export async function execAgentUpdate(args) {
|
|
|
501
531
|
if (p.aid !== undefined) {
|
|
502
532
|
return { error: 'aid 不可修改(AUN 身份绑定,如需换 AID 请删除后重建)', code: 'INVALID_ARGS' };
|
|
503
533
|
}
|
|
504
|
-
if (p.avatar !== undefined) {
|
|
505
|
-
const otherKeys = Object.keys(p).filter(k => k !== 'avatar');
|
|
506
|
-
if (otherKeys.length > 0) {
|
|
507
|
-
return { error: 'avatar 不能与其他 patch 字段同时提交,请拆分为两次 update', code: 'INVALID_ARGS' };
|
|
508
|
-
}
|
|
509
|
-
}
|
|
510
534
|
if (p.name !== undefined) {
|
|
511
535
|
const otherKeys = Object.keys(p).filter(k => k !== 'name');
|
|
512
536
|
if (otherKeys.length > 0) {
|
|
@@ -522,13 +546,6 @@ export async function execAgentUpdate(args) {
|
|
|
522
546
|
const config = cfgRead(ConfigTarget.Agent, { self: a.aid });
|
|
523
547
|
if (!config)
|
|
524
548
|
return { error: `Agent "${a.aid}" not found`, code: 'NOT_FOUND' };
|
|
525
|
-
if (p.avatar !== undefined) {
|
|
526
|
-
const result = await uploadAvatar(a.aid, p.avatar, { aunPath: resolveRoot() });
|
|
527
|
-
if (!result.ok) {
|
|
528
|
-
return { error: `头像上传失败: ${result.error}`, code: result.code ?? 'UPLOAD_FAILED' };
|
|
529
|
-
}
|
|
530
|
-
return { data: { aid: a.aid, avatar: result.publicUrl, saved: true } };
|
|
531
|
-
}
|
|
532
549
|
let touched = false;
|
|
533
550
|
const data = { aid: a.aid };
|
|
534
551
|
const hasConfigPatch = p.active_baseagent !== undefined || p.baseagents !== undefined || p.projects !== undefined || p.owners !== undefined || p.chatmode !== undefined || p.channels !== undefined || p.channelOwners !== undefined;
|
|
@@ -564,7 +581,19 @@ export async function execAgentUpdate(args) {
|
|
|
564
581
|
delete config.projects;
|
|
565
582
|
}
|
|
566
583
|
if (p.owners !== undefined) {
|
|
567
|
-
|
|
584
|
+
if (!Array.isArray(p.owners) || p.owners.length === 0) {
|
|
585
|
+
return { error: 'owners must contain at least one owner', code: 'LAST_OWNER_PROTECTED' };
|
|
586
|
+
}
|
|
587
|
+
const owners = [];
|
|
588
|
+
for (const rawOwner of p.owners) {
|
|
589
|
+
if (typeof rawOwner !== 'string' || !rawOwner.trim() || !isValidAid(rawOwner.trim())) {
|
|
590
|
+
return { error: `Invalid owner AID: ${JSON.stringify(rawOwner)}`, code: 'INVALID_ARGS' };
|
|
591
|
+
}
|
|
592
|
+
const owner = rawOwner.trim();
|
|
593
|
+
if (!owners.includes(owner))
|
|
594
|
+
owners.push(owner);
|
|
595
|
+
}
|
|
596
|
+
config.owners = owners;
|
|
568
597
|
touched = true;
|
|
569
598
|
}
|
|
570
599
|
if (p.chatmode !== undefined) {
|
|
@@ -156,6 +156,14 @@ function parseStatsCommand(argv, source) {
|
|
|
156
156
|
}
|
|
157
157
|
function parseAgentCommand(argv, source) {
|
|
158
158
|
const subcmd = argv[1];
|
|
159
|
+
if (subcmd === 'persona') {
|
|
160
|
+
const tail = argv.at(-2) === '--format' && argv.at(-1) === 'json' ? argv.slice(0, -2) : argv;
|
|
161
|
+
const isShow = tail.length === 3 && tail[2] === 'show';
|
|
162
|
+
const isWrite = tail.length === 4 && (tail[2] === '--text' || tail[2] === '--file');
|
|
163
|
+
return isShow || isWrite
|
|
164
|
+
? recognized('agent.persona', 'agent', source, {}, argv)
|
|
165
|
+
: raw(source, argv, 'Invalid agent persona command');
|
|
166
|
+
}
|
|
159
167
|
const parsed = parseArgs(argv.slice(2));
|
|
160
168
|
const args = parsed.args;
|
|
161
169
|
if (parsed.positionals.length > 0)
|
|
@@ -170,6 +178,16 @@ function parseAgentCommand(argv, source) {
|
|
|
170
178
|
}
|
|
171
179
|
function parseAidCommand(argv, source) {
|
|
172
180
|
const subcmd = argv[1];
|
|
181
|
+
if (subcmd === 'avatar') {
|
|
182
|
+
const tail = argv.slice(2);
|
|
183
|
+
const aid = tail[0];
|
|
184
|
+
const sourceFlag = tail[1];
|
|
185
|
+
if (!aid || (sourceFlag !== '--file' && sourceFlag !== '--url') || !tail[2] || tail[2].startsWith('-')
|
|
186
|
+
|| (tail.length !== 3 && !(tail.length === 5 && tail[3] === '--format' && tail[4] === 'json'))) {
|
|
187
|
+
return raw(source, argv, 'Invalid aid avatar command');
|
|
188
|
+
}
|
|
189
|
+
return recognized('ec.aid.avatar', 'agent', source, { aid, ...(sourceFlag === '--file' ? { file: tail[2] } : { url: tail[2] }) }, argv, true);
|
|
190
|
+
}
|
|
173
191
|
const parsed = parseArgs(argv.slice(2));
|
|
174
192
|
const args = parsed.args;
|
|
175
193
|
if (parsed.positionals.length > 0)
|
|
@@ -1399,17 +1399,17 @@ export class CommandHandler {
|
|
|
1399
1399
|
const nextStr = reg.trigger.nextFireAt ? new Date(reg.trigger.nextFireAt).toLocaleString() : '未计算';
|
|
1400
1400
|
return `✅ 触发器已注册:**${reg.trigger.name}**\n下次触发:${nextStr}`;
|
|
1401
1401
|
}
|
|
1402
|
-
return `❌ 未知子命令。用法:
|
|
1403
|
-
/trigger — 查看活跃触发器
|
|
1404
|
-
/trigger list [--all] — 查看所有触发器
|
|
1405
|
-
/trigger create <参数> — 创建触发器(set 为兼容别名)
|
|
1406
|
-
/trigger update <名称|ID> <参数> — 修改触发器
|
|
1407
|
-
常用参数:--delay/--at/--cron/--every、--prompt、--description、--model、--effort、--permission(省略则继承;update 可用 inherit 清除覆盖)、--feedback、--target-channel/--target-channel-id、--trigger-thread、--baseagent、--on-error、--retry-attempts/--retry-backoff、--script-runtime/--script-args/--script-timeout
|
|
1408
|
-
/trigger enable <名称|ID> — 启用触发器
|
|
1409
|
-
/trigger disable <名称|ID> — 暂停触发器
|
|
1410
|
-
/trigger show <名称|ID> — 查看触发器详情
|
|
1411
|
-
/trigger run <名称|ID> [--dry-run] — 手动触发
|
|
1412
|
-
/trigger cancel <名称|ID> — 禁用触发器(disable 的兼容别名)
|
|
1402
|
+
return `❌ 未知子命令。用法:
|
|
1403
|
+
/trigger — 查看活跃触发器
|
|
1404
|
+
/trigger list [--all] — 查看所有触发器
|
|
1405
|
+
/trigger create <参数> — 创建触发器(set 为兼容别名)
|
|
1406
|
+
/trigger update <名称|ID> <参数> — 修改触发器
|
|
1407
|
+
常用参数:--delay/--at/--cron/--every、--prompt、--description、--model、--effort、--permission(省略则继承;update 可用 inherit 清除覆盖)、--feedback、--target-channel/--target-channel-id、--trigger-thread、--baseagent、--on-error、--retry-attempts/--retry-backoff、--script-runtime/--script-args/--script-timeout
|
|
1408
|
+
/trigger enable <名称|ID> — 启用触发器
|
|
1409
|
+
/trigger disable <名称|ID> — 暂停触发器
|
|
1410
|
+
/trigger show <名称|ID> — 查看触发器详情
|
|
1411
|
+
/trigger run <名称|ID> [--dry-run] — 手动触发
|
|
1412
|
+
/trigger cancel <名称|ID> — 禁用触发器(disable 的兼容别名)
|
|
1413
1413
|
/trigger delete <名称|ID> — 删除触发器`;
|
|
1414
1414
|
}
|
|
1415
1415
|
async handleTrigger(content, channel, channelId, peerId, isAdmin, messageId, chatType, threadId, isDaemonOwner = false, canApprovePersistentFullAccess = isDaemonOwner) {
|
|
@@ -1946,10 +1946,10 @@ export class CommandHandler {
|
|
|
1946
1946
|
if (sessionSelfAid && sessionSelfAid !== grantSelfAid) {
|
|
1947
1947
|
return { ok: false, code: 'INVALID_DELEGATION', error: 'Delegation self agent does not match the active session' };
|
|
1948
1948
|
}
|
|
1949
|
-
const requiresSelfAid = parsed.command.kind === 'show' || parsed.command.kind === 'agentmd.put';
|
|
1949
|
+
const requiresSelfAid = parsed.command.kind === 'show' || parsed.command.kind === 'agentmd.put' || parsed.command.kind === 'avatar';
|
|
1950
1950
|
if (requiresSelfAid
|
|
1951
1951
|
&& parsed.command.aid !== grantSelfAid
|
|
1952
|
-
&& !hasTrustedFullAccessDelegation(delegation.grant)) {
|
|
1952
|
+
&& (parsed.command.kind === 'avatar' || !hasTrustedFullAccessDelegation(delegation.grant))) {
|
|
1953
1953
|
return { ok: false, code: 'SCOPE_MISMATCH', error: 'Only the current Agent AID can be targeted' };
|
|
1954
1954
|
}
|
|
1955
1955
|
let conversationId;
|
|
@@ -1988,12 +1988,87 @@ export class CommandHandler {
|
|
|
1988
1988
|
});
|
|
1989
1989
|
if (!decision.allow)
|
|
1990
1990
|
return { ok: false, code: decision.code, error: decision.reason };
|
|
1991
|
+
let avatarBuffer;
|
|
1992
|
+
if (parsed.command.kind === 'avatar' && parsed.command.filePath) {
|
|
1993
|
+
const filePath = String(parsed.command.filePath || '').trim();
|
|
1994
|
+
if (typeof session.projectPath !== 'string' || !session.projectPath.trim()) {
|
|
1995
|
+
return { ok: false, code: 'INVALID_AVATAR_PATH', error: 'Avatar file requires a current session project path' };
|
|
1996
|
+
}
|
|
1997
|
+
const projectPath = path.resolve(session.projectPath);
|
|
1998
|
+
const managedTempPath = this.getManagedSessionTempDir(session);
|
|
1999
|
+
const resolvedPath = path.isAbsolute(filePath) ? path.resolve(filePath) : path.resolve(projectPath, filePath);
|
|
2000
|
+
const allowedRoots = [projectPath, managedTempPath].filter((value) => !!value);
|
|
2001
|
+
if (!filePath || !allowedRoots.some(root => isSameOrDescendant(resolvedPath, root))) {
|
|
2002
|
+
return { ok: false, code: 'INVALID_AVATAR_PATH', error: 'Avatar file must be inside the current session project or managed TMPDIR' };
|
|
2003
|
+
}
|
|
2004
|
+
if (isHClassPath(resolvedPath) || isLClassPath(resolvedPath)) {
|
|
2005
|
+
return { ok: false, code: 'PROTECTED_AVATAR_PATH', error: 'Avatar file cannot be read from a protected EvolCore path' };
|
|
2006
|
+
}
|
|
2007
|
+
let fd;
|
|
2008
|
+
try {
|
|
2009
|
+
const realProjectPath = fs.realpathSync(projectPath);
|
|
2010
|
+
const realPath = fs.realpathSync(resolvedPath);
|
|
2011
|
+
let realManagedTempPath;
|
|
2012
|
+
try {
|
|
2013
|
+
realManagedTempPath = managedTempPath ? fs.realpathSync(managedTempPath) : undefined;
|
|
2014
|
+
}
|
|
2015
|
+
catch { }
|
|
2016
|
+
const realAllowedRoots = [realProjectPath, realManagedTempPath].filter((value) => !!value);
|
|
2017
|
+
if (!realAllowedRoots.some(root => isSameOrDescendant(realPath, root))) {
|
|
2018
|
+
return { ok: false, code: 'INVALID_AVATAR_PATH', error: 'Avatar file resolves outside the current session project or managed TMPDIR' };
|
|
2019
|
+
}
|
|
2020
|
+
if (isHClassPath(realPath) || isLClassPath(realPath)) {
|
|
2021
|
+
return { ok: false, code: 'PROTECTED_AVATAR_PATH', error: 'Avatar file cannot be read from a protected EvolCore path' };
|
|
2022
|
+
}
|
|
2023
|
+
const stat = fs.lstatSync(resolvedPath);
|
|
2024
|
+
if (stat.isSymbolicLink() || !stat.isFile()) {
|
|
2025
|
+
return { ok: false, code: 'INVALID_AVATAR_FILE', error: 'Avatar source must be a regular non-symlink file' };
|
|
2026
|
+
}
|
|
2027
|
+
if (realManagedTempPath && isSameOrDescendant(realPath, realManagedTempPath) && stat.nlink > 1) {
|
|
2028
|
+
return { ok: false, code: 'INVALID_AVATAR_PATH', error: 'Avatar source cannot be a hard link inside managed TMPDIR' };
|
|
2029
|
+
}
|
|
2030
|
+
if (stat.size < 1 || stat.size > 500 * 1024) {
|
|
2031
|
+
return { ok: false, code: 'INVALID_AVATAR_FILE', error: 'Avatar source must be between 1 byte and 500KB' };
|
|
2032
|
+
}
|
|
2033
|
+
// Open the user-supplied node with no-follow semantics after checking
|
|
2034
|
+
// its canonical ancestors; the bytes passed to the uploader come from
|
|
2035
|
+
// this descriptor rather than from a second path lookup.
|
|
2036
|
+
fd = fs.openSync(resolvedPath, fs.constants.O_RDONLY | fs.constants.O_NOFOLLOW);
|
|
2037
|
+
const finalStat = fs.fstatSync(fd);
|
|
2038
|
+
if (!finalStat.isFile() || finalStat.size < 1 || finalStat.size > 500 * 1024 || finalStat.nlink !== stat.nlink) {
|
|
2039
|
+
return { ok: false, code: 'INVALID_AVATAR_FILE', error: 'Avatar source changed during validation' };
|
|
2040
|
+
}
|
|
2041
|
+
avatarBuffer = fs.readFileSync(fd);
|
|
2042
|
+
}
|
|
2043
|
+
catch (error) {
|
|
2044
|
+
return { ok: false, code: 'INVALID_AVATAR_FILE', error: error instanceof Error ? error.message : String(error) };
|
|
2045
|
+
}
|
|
2046
|
+
finally {
|
|
2047
|
+
if (fd !== undefined)
|
|
2048
|
+
fs.closeSync(fd);
|
|
2049
|
+
}
|
|
2050
|
+
}
|
|
1991
2051
|
try {
|
|
2052
|
+
const result = await executeManagedAidOperation(parsed.command, {
|
|
2053
|
+
showView: managedAidShowViewForRole(subject.role),
|
|
2054
|
+
...(avatarBuffer ? { avatarBuffer } : {}),
|
|
2055
|
+
});
|
|
2056
|
+
if (result.kind === 'avatar' && result.avatar) {
|
|
2057
|
+
this.eventBus.publish({
|
|
2058
|
+
type: 'agent:avatar_updated',
|
|
2059
|
+
aid: result.aid,
|
|
2060
|
+
avatar: result.avatar,
|
|
2061
|
+
...(result.sha256 ? { sha256: result.sha256 } : {}),
|
|
2062
|
+
...(result.mime ? { mime: result.mime } : {}),
|
|
2063
|
+
...(result.width ? { width: result.width } : {}),
|
|
2064
|
+
...(result.height ? { height: result.height } : {}),
|
|
2065
|
+
...(result.aspectRatio ? { aspectRatio: result.aspectRatio } : {}),
|
|
2066
|
+
timestamp: Date.now(),
|
|
2067
|
+
});
|
|
2068
|
+
}
|
|
1992
2069
|
return {
|
|
1993
2070
|
ok: true,
|
|
1994
|
-
result
|
|
1995
|
-
showView: managedAidShowViewForRole(subject.role),
|
|
1996
|
-
}),
|
|
2071
|
+
result,
|
|
1997
2072
|
};
|
|
1998
2073
|
}
|
|
1999
2074
|
catch (error) {
|
|
@@ -79,6 +79,7 @@ const ENTRIES = [
|
|
|
79
79
|
{ id: 'chatmode.agent.nothuman', type: 'setting', contexts: ['agent'], name: 'chatmode', label: 'Agent 非人类对话模式', section: 'agent', order: 43, query: { cmd: '/chatmode', args: { scope: 'agent', field: 'nothuman' }, operation: 'chatmode.current', scope: 'agent' }, options: { cmd: '/chatmode', args: { scope: 'agent', field: 'nothuman' }, operation: 'chatmode.current', scope: 'agent' }, update: { cmd: '/chatmode', args: { scope: 'agent', field: 'nothuman' }, operation: 'chatmode.update', scope: 'agent' }, control: 'select' },
|
|
80
80
|
{ id: 'activity.agent', type: 'setting', contexts: ['agent'], name: 'activity', label: 'Agent 中间输出', section: 'agent', order: 50, query: { cmd: '/activity', args: { scope: 'agent' }, operation: 'activity.current', scope: 'agent' }, options: { cmd: '/activity', args: { scope: 'agent' }, operation: 'activity.current', scope: 'agent' }, update: { cmd: '/activity', args: { scope: 'agent' }, operation: 'activity.update', scope: 'agent' }, control: 'select' },
|
|
81
81
|
{ id: 'observable.agent', type: 'setting', contexts: ['agent'], name: 'observable', label: '观察模式', section: 'agent', order: 60, query: { cmd: '/observable', operation: 'observable.current', scope: 'agent' }, update: { cmd: '/observable', operation: 'observable.update', scope: 'agent' }, valueSchema: { type: 'boolean' }, control: 'toggle' },
|
|
82
|
+
{ id: 'aid.avatar', type: 'action', contexts: ['agent'], name: 'aid', label: '更换头像', section: 'agent', order: 70, action: { cmd: '/aid', action: 'avatar', operation: 'ec.aid.avatar', scope: 'agent' }, valueSchema: { type: 'object', properties: { data: { type: 'string' }, url: { type: 'string', format: 'uri' } }, oneOf: [{ required: ['data'] }, { required: ['url'] }] }, control: 'action' },
|
|
82
83
|
{ id: 'system.status', type: 'list', contexts: ['process'], name: 'system', label: '系统状态', section: 'process', order: 10, query: { cmd: '/system', operation: 'system.status', scope: 'process' }, control: 'list' },
|
|
83
84
|
{ id: 'system.monitor', type: 'list', contexts: ['process'], name: 'system', label: '运行监控快照', section: 'process', order: 11, query: { cmd: '/system', args: { view: 'monitor' }, operation: 'system.status', scope: 'process' }, control: 'list' },
|
|
84
85
|
{ id: 'system.logs', type: 'list', contexts: ['process'], name: 'system', label: 'Daemon 日志', section: 'process', order: 12, query: { cmd: '/system', args: { view: 'logs' }, operation: 'system.status', scope: 'process' }, options: { cmd: '/system', args: { option: 'logs' }, operation: 'system.status', scope: 'process', mode: 'dynamic' }, control: 'list' },
|
|
@@ -19,7 +19,7 @@ import { scanInstances } from '../../utils/instance-registry.js';
|
|
|
19
19
|
import { isFullAccessEnabled, loadDefaults, loadDaemonConfig } from '../../config-store.js';
|
|
20
20
|
import { WEB_PACKAGE_NAME } from '../../product.js';
|
|
21
21
|
import { read as cfgRead, resolveEffective, resolveEffectiveFieldWithSource, routeFieldPath, write as cfgWrite, ConfigTarget, } from '../../config/config-manager.js';
|
|
22
|
-
import { execAgentAction, execAgentQuery, execAgentOptions, resolveProjectPath } from './agent-control.js';
|
|
22
|
+
import { execAgentAction, execAgentQuery, execAgentOptions, execAidAvatar, resolveProjectPath } from './agent-control.js';
|
|
23
23
|
import { gatewayList, gatewayUpdate, gatewayDelete, gatewayTest, gatewayModels, gatewaySetPrice, gatewaySyncEnv } from '../../config/gateway-config.js';
|
|
24
24
|
import { displaySessionTitle } from '../session/session-title.js';
|
|
25
25
|
import { buildSessionTurnList } from '../session/session-turns.js';
|
|
@@ -1028,6 +1028,8 @@ function buildMenuIntent(verb, cmdBase, args, action, value, fromControlChannel
|
|
|
1028
1028
|
if (action === 'disable')
|
|
1029
1029
|
return intent('agent.disable', 'control', { action });
|
|
1030
1030
|
}
|
|
1031
|
+
if (cmdBase === '/aid' && action === 'avatar')
|
|
1032
|
+
return intent('ec.aid.avatar', 'agent', { action });
|
|
1031
1033
|
if (cmdBase === '/trigger') {
|
|
1032
1034
|
if (action === 'create' || action === 'set')
|
|
1033
1035
|
return intent('trigger.create', 'relation', { action });
|
|
@@ -1287,7 +1289,11 @@ function menuResultFailure(error) {
|
|
|
1287
1289
|
return { error: normalized.message, code: normalized.code, ...(normalized.data !== undefined ? { data: normalized.data } : {}) };
|
|
1288
1290
|
}
|
|
1289
1291
|
function resolveExternalMenuAgentChannel(owner, payload, fallbackChannel, required) {
|
|
1290
|
-
const requestedAid = typeof payload?.agent === 'string'
|
|
1292
|
+
const requestedAid = typeof payload?.agent === 'string' && payload.agent.trim()
|
|
1293
|
+
? payload.agent.trim()
|
|
1294
|
+
: typeof payload?.args?.aid === 'string'
|
|
1295
|
+
? payload.args.aid.trim()
|
|
1296
|
+
: '';
|
|
1291
1297
|
if (!requestedAid) {
|
|
1292
1298
|
return required
|
|
1293
1299
|
? { error: '缺少顶层 agent 参数', code: 'MISSING_AID' }
|
|
@@ -3061,7 +3067,7 @@ export async function execMenuAction(cmd, action, args, channel, channelId, user
|
|
|
3061
3067
|
return menuResultFailure(error);
|
|
3062
3068
|
}
|
|
3063
3069
|
}
|
|
3064
|
-
if (cmdBase === '/agent'
|
|
3070
|
+
if ((cmdBase === '/agent' || cmdBase === '/aid') && !args?.aid) {
|
|
3065
3071
|
const selfAid = this.getOwningAgent?.(channel)?.aid;
|
|
3066
3072
|
if (selfAid)
|
|
3067
3073
|
args = { ...(args ?? {}), aid: selfAid };
|
|
@@ -3096,6 +3102,30 @@ export async function execMenuAction(cmd, action, args, channel, channelId, user
|
|
|
3096
3102
|
return { error: `不支持 gateway action: ${action}`, code: 'NOT_SUPPORTED' };
|
|
3097
3103
|
}
|
|
3098
3104
|
const isMenuAdmin = isManagementRole(authIdentity.role);
|
|
3105
|
+
// ── /aid avatar ──
|
|
3106
|
+
// The AID namespace is the only avatar mutation surface.
|
|
3107
|
+
if (cmdBase === '/aid') {
|
|
3108
|
+
if (action !== 'avatar')
|
|
3109
|
+
return { error: `不支持 aid action: ${action}`, code: 'NOT_SUPPORTED' };
|
|
3110
|
+
const result = await execAidAvatar(args);
|
|
3111
|
+
if (!('error' in result)) {
|
|
3112
|
+
const data = result.data;
|
|
3113
|
+
if (data?.avatar) {
|
|
3114
|
+
this.eventBus.publish({
|
|
3115
|
+
type: 'agent:avatar_updated',
|
|
3116
|
+
aid: data.aid,
|
|
3117
|
+
avatar: data.avatar,
|
|
3118
|
+
...(data.sha256 !== undefined ? { sha256: data.sha256 } : {}),
|
|
3119
|
+
...(data.mime !== undefined ? { mime: data.mime } : {}),
|
|
3120
|
+
...(data.width !== undefined ? { width: data.width } : {}),
|
|
3121
|
+
...(data.height !== undefined ? { height: data.height } : {}),
|
|
3122
|
+
...(data.aspectRatio !== undefined ? { aspectRatio: data.aspectRatio } : {}),
|
|
3123
|
+
timestamp: Date.now(),
|
|
3124
|
+
});
|
|
3125
|
+
}
|
|
3126
|
+
}
|
|
3127
|
+
return result;
|
|
3128
|
+
}
|
|
3099
3129
|
// ── /agent action ──
|
|
3100
3130
|
// 控制 channel:验 evolcore.owners,可执行进程级(create/delete/enable/disable)+ 自管理(update/reload)。
|
|
3101
3131
|
// agent channel:闸门已挡掉进程级 + 跨 agent,仅 update/reload 能到此;reload 允许 owner/admin,update 仅 owner。
|
|
@@ -4157,9 +4187,9 @@ async function execMenuForSystemControl(payload, context) {
|
|
|
4157
4187
|
const isRoleRequest = name === 'role' || compatCmd === '/role';
|
|
4158
4188
|
const isConnectRequest = name === 'connect' || compatCmd === '/connect';
|
|
4159
4189
|
const requiresAgentTarget = isRoleRequest || isConnectRequest || [
|
|
4160
|
-
'observable', 'baseagent', 'model', 'effort', 'trigger', 'capability', 'group',
|
|
4190
|
+
'observable', 'baseagent', 'model', 'effort', 'trigger', 'capability', 'group', 'aid',
|
|
4161
4191
|
].includes(name)
|
|
4162
|
-
|| ['/observable', '/baseagent', '/model', '/effort', '/trigger', '/capability', '/group'].includes(compatCmd ?? '')
|
|
4192
|
+
|| ['/observable', '/baseagent', '/model', '/effort', '/trigger', '/capability', '/group', '/aid'].includes(compatCmd ?? '')
|
|
4163
4193
|
|| (!isProcessLevel && requestedContextKind !== undefined && requestedContextKind !== 'process');
|
|
4164
4194
|
const target = isProcessLevel
|
|
4165
4195
|
? { channel: SYSTEM_CONTROL_CHANNEL }
|
|
@@ -4169,6 +4199,12 @@ async function execMenuForSystemControl(payload, context) {
|
|
|
4169
4199
|
const targetChannel = target.channel;
|
|
4170
4200
|
const targetAid = this.getOwningAgent?.(targetChannel)?.aid
|
|
4171
4201
|
?? this.getDaemonTriggerOwnerAid?.(targetChannel);
|
|
4202
|
+
if (name === 'aid' || compatCmd === '/aid') {
|
|
4203
|
+
const requestedAid = typeof payload?.args?.aid === 'string' ? payload.args.aid.trim().replace(/^@/, '') : '';
|
|
4204
|
+
if (requestedAid && targetAid && requestedAid !== targetAid) {
|
|
4205
|
+
return ecwebErr(id, name, 'INVALID_ARGUMENT', 'args.aid 必须与目标 Agent 一致');
|
|
4206
|
+
}
|
|
4207
|
+
}
|
|
4172
4208
|
const trustedSubject = buildAuthSubject({
|
|
4173
4209
|
selfAid: targetAid,
|
|
4174
4210
|
actorId: context.actorAid,
|
|
@@ -3223,10 +3223,10 @@ export async function handleSlashCommand(content, channel, channelId, sendMessag
|
|
|
3223
3223
|
// /slist cli — 仅 CLI 会话(未导入的)
|
|
3224
3224
|
if (normalizedContent === '/slist' || normalizedContent === '/slist cli') {
|
|
3225
3225
|
if (!session) {
|
|
3226
|
-
return { kind: 'command.error', text: `❌ 当前没有活跃会话
|
|
3227
|
-
|
|
3228
|
-
请先执行以下操作之一:
|
|
3229
|
-
1. 发送任意消息 - 自动创建新会话
|
|
3226
|
+
return { kind: 'command.error', text: `❌ 当前没有活跃会话
|
|
3227
|
+
|
|
3228
|
+
请先执行以下操作之一:
|
|
3229
|
+
1. 发送任意消息 - 自动创建新会话
|
|
3230
3230
|
2. /new [名称] - 创建命名会话` };
|
|
3231
3231
|
}
|
|
3232
3232
|
const showCliOnly = normalizedContent === '/slist cli';
|
|
@@ -3497,11 +3497,11 @@ export async function handleSlashCommand(content, channel, channelId, sendMessag
|
|
|
3497
3497
|
if (!newName)
|
|
3498
3498
|
return { kind: 'command.result', text: '用法: /name <新名称> 或 /rename <新名称>' };
|
|
3499
3499
|
if (!session) {
|
|
3500
|
-
return { kind: 'command.error', text: `❌ 当前没有活跃会话
|
|
3501
|
-
|
|
3502
|
-
请先执行以下操作之一:
|
|
3503
|
-
1. 发送任意消息 - 自动创建新会话
|
|
3504
|
-
2. /new [名称] - 创建命名会话
|
|
3500
|
+
return { kind: 'command.error', text: `❌ 当前没有活跃会话
|
|
3501
|
+
|
|
3502
|
+
请先执行以下操作之一:
|
|
3503
|
+
1. 发送任意消息 - 自动创建新会话
|
|
3504
|
+
2. /new [名称] - 创建命名会话
|
|
3505
3505
|
3. /session <名称> - 切换到已有会话` };
|
|
3506
3506
|
}
|
|
3507
3507
|
const existing = await this.sessionManager.getSessionByName(channel, channelId, newName);
|
|
@@ -796,6 +796,11 @@ const CATALOG = [
|
|
|
796
796
|
fields: [
|
|
797
797
|
{ path: 'aid', type: 'string' },
|
|
798
798
|
{ path: 'avatar', type: 'string' },
|
|
799
|
+
{ path: 'sha256', type: 'string', optional: true },
|
|
800
|
+
{ path: 'mime', type: 'string', optional: true },
|
|
801
|
+
{ path: 'width', type: 'number', optional: true },
|
|
802
|
+
{ path: 'height', type: 'number', optional: true },
|
|
803
|
+
{ path: 'aspectRatio', type: 'number', optional: true },
|
|
799
804
|
{ path: 'timestamp', type: 'number', optional: true },
|
|
800
805
|
],
|
|
801
806
|
},
|
package/dist/core/evolagent.js
CHANGED
|
@@ -275,9 +275,9 @@ export class EvolAgent {
|
|
|
275
275
|
return `agent-files:${this.aid}`;
|
|
276
276
|
}
|
|
277
277
|
/**
|
|
278
|
-
* 读取 personal/persona.md 内容。走 fileCache(mtime 门控):persona
|
|
279
|
-
*
|
|
280
|
-
* stat 比对、变了自动重读。文件不存在返回 null。
|
|
278
|
+
* 读取 personal/persona.md 内容。走 fileCache(mtime 门控):persona 可由
|
|
279
|
+
* `ec agent persona` 或 agent 自己更新,与 working memory 一样改了即应生效,
|
|
280
|
+
* 故每次读 stat 比对、变了自动重读。文件不存在返回 null。
|
|
281
281
|
*/
|
|
282
282
|
getPersona() {
|
|
283
283
|
const personaPath = path.join(agentPersonalDir(this.aid), 'persona.md');
|