evolcore 0.0.3 → 0.0.5
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 +83 -0
- package/README.md +45 -10
- package/bin/ec-safe-output.js +89 -24
- package/dist/agents/baseagent.js +46 -0
- package/dist/agents/claude-runner.js +1 -31
- package/dist/agents/codex-app-server-client.js +68 -0
- package/dist/agents/codex-runner.js +157 -5
- package/dist/agents/runner-types.js +2 -2
- package/dist/aun/aid/agentmd.js +79 -0
- package/dist/aun/aid/encryption-seed-policy.js +29 -0
- package/dist/aun/aid/index.js +1 -1
- package/dist/aun/aid/store.js +2 -5
- package/dist/channels/aun.js +220 -112
- package/dist/channels/daemon.js +18 -4
- package/dist/channels/dingtalk.js +52 -137
- package/dist/channels/feishu.js +56 -4
- package/dist/channels/qqbot.js +23 -1
- package/dist/channels/wechat.js +303 -155
- package/dist/channels/wecom.js +383 -7
- package/dist/cli/aun-commands.js +11 -11
- package/dist/cli/daemon-commands.js +46 -15
- package/dist/cli/handoff-command.js +2 -1
- package/dist/cli/init-channel.js +185 -67
- package/dist/cli/init.js +49 -27
- package/dist/cli/trigger-command.js +92 -11
- package/dist/config/access-policy-domain.js +18 -0
- package/dist/config/access-policy.js +110 -0
- package/dist/config/builtin-role-templates.js +27 -14
- package/dist/config/builtin-roles.js +25 -6
- package/dist/config/config-field-policy.js +17 -5
- package/dist/config/config-manager.js +198 -22
- package/dist/config/config-operation-service.js +35 -38
- package/dist/config/contact-bind-code.js +274 -0
- package/dist/config/contact-book-store.js +173 -5
- package/dist/config/contact-book.js +32 -0
- package/dist/config/contact-operation-service.js +9 -3
- package/dist/config/contact-request-service.js +331 -0
- package/dist/config/peer-role-resolver.js +3 -1
- package/dist/config/schema-registry.js +49 -24
- package/dist/config-store.js +14 -2
- package/dist/core/auth/agent-delegation.js +105 -11
- package/dist/core/auth/authorization-audit.js +6 -0
- package/dist/core/auth/operation-authorizer.js +8 -0
- package/dist/core/auth/operation-catalog.js +51 -3
- package/dist/core/auth/trigger-authorization.js +15 -0
- package/dist/core/bootstrap-service.js +2 -9
- package/dist/core/channel-loader.js +6 -2
- package/dist/core/command/command-handler.js +10 -13
- package/dist/core/command/connect-menu.js +229 -26
- package/dist/core/command/evol-menu-version-gate.js +38 -0
- package/dist/core/command/group-menu.js +421 -0
- package/dist/core/command/menu-handler.js +269 -85
- package/dist/core/command/menu-protocol.js +8 -2
- package/dist/core/command/menu-token-store.js +102 -0
- package/dist/core/command/role-menu.js +16 -0
- package/dist/core/command/slash-gate.js +1 -1
- package/dist/core/command/slash-handler.js +158 -31
- package/dist/core/daemon-file-cache.js +28 -0
- package/dist/core/event-catalog.js +29 -0
- package/dist/core/evolagent-registry.js +4 -39
- package/dist/core/handoff/runtime.js +162 -37
- package/dist/core/handoff/store.js +46 -2
- package/dist/core/handoff/types.js +1 -0
- package/dist/core/inference/text-inference.js +16 -74
- package/dist/core/message/file-markers.js +7 -0
- package/dist/core/message/im-renderer.js +18 -14
- package/dist/core/message/inbound-admission.js +134 -0
- package/dist/core/message/message-bridge.js +616 -74
- package/dist/core/message/message-log.js +1 -0
- package/dist/core/message/message-queue.js +185 -10
- package/dist/core/message/peer-mode.js +7 -8
- package/dist/core/message/response-engine.js +381 -79
- package/dist/core/permission/approval-gateway.js +17 -10
- package/dist/core/permission/ec-command-parser.js +101 -46
- package/dist/core/permission/tool-policy.js +69 -24
- package/dist/core/protected-paths.js +36 -11
- package/dist/core/session/session-fs-store.js +19 -4
- package/dist/core/session/session-manager.js +232 -4
- package/dist/core/session/session-mapper.js +2 -0
- package/dist/core/session/session-renew.js +125 -69
- package/dist/core/session/session-turn-coordinator.js +5 -1
- package/dist/eck/kit-renderer.js +12 -1
- package/dist/eck/message-renderer.js +79 -1
- package/dist/index.js +224 -89
- package/dist/ipc.js +81 -4
- package/dist/paths.js +3 -0
- package/dist/response-system/config-resolver.js +29 -0
- package/dist/response-system/coordinator.js +8 -32
- package/dist/response-system/engines/v1/proactive-flow.js +1 -1
- package/dist/response-system/index.js +1 -0
- package/dist/response-system/modes/single-session/index.js +7 -4
- package/dist/trigger/parser.js +55 -15
- package/dist/trigger/patch.js +4 -1
- package/dist/trigger/scheduler.js +441 -39
- package/dist/utils/cross-platform.js +8 -2
- package/dist/utils/error-dict.json +7 -0
- package/dist/utils/evolcore-version.js +21 -0
- package/dist/utils/logger.js +2 -2
- package/dist/utils/process-introspect.js +19 -3
- package/dist/utils/stable-semver.js +21 -0
- package/dist/utils/stats.js +33 -9
- package/kits/docs/channels/aun.md +4 -13
- package/kits/docs/evolcore/INDEX.md +1 -1
- package/kits/docs/evolcore/config.md +47 -2
- package/kits/docs/evolcore/contact.md +8 -3
- package/kits/docs/evolcore/group-rules.md +46 -4
- package/kits/docs/evolcore/group.md +4 -4
- package/kits/docs/evolcore/msg.md +5 -5
- package/kits/docs/evolcore/trigger.md +25 -8
- package/kits/docs/path-registry.md +36 -17
- package/kits/eck_message_manifest.json +12 -1
- package/kits/migrations/migrate-contact-book-v2.mjs +7 -0
- package/kits/rules/01-overview.md +17 -7
- package/kits/rules/02-navigation.md +38 -18
- package/kits/rules/03-identity.md +28 -24
- package/kits/rules/04-relation.md +44 -28
- package/kits/rules/05-venue.md +31 -15
- package/kits/rules/06-channel.md +11 -7
- package/kits/schemas/_meta.json +18 -7
- package/kits/schemas/agent-config.schema.7.json +303 -0
- package/kits/schemas/agent-config.schema.8.json +304 -0
- package/kits/schemas/agent-config.schema.9.json +364 -0
- package/kits/schemas/contact-book.schema.3.json +67 -0
- package/kits/schemas/daemon.schema.1.json +3 -2
- package/kits/schemas/daemon.schema.2.json +101 -0
- package/kits/schemas/daemon.schema.3.json +123 -0
- package/kits/schemas/defaults.schema.2.json +85 -0
- package/kits/schemas/defaults.schema.3.json +73 -0
- package/kits/schemas/relation-config.schema.6.json +46 -0
- package/kits/schemas/relation-config.schema.7.json +59 -0
- package/kits/schemas/single-session.schema.2.json +57 -0
- package/kits/templates/message-fragments/handoff-context-to-target.md +9 -0
- package/kits/templates/message-fragments/handoff-request-to-target.md +14 -8
- package/kits/templates/message-fragments/handoff-response-to-origin.md +5 -6
- package/kits/templates/roles/admin.json +46 -0
- package/kits/templates/roles/member.json +4 -0
- package/kits/templates/roles/visitor.json +4 -0
- package/kits/templates/system-fragments/channel.md +12 -2
- package/kits/templates/system-fragments/identity.md +3 -1
- package/kits/templates/system-fragments/relation.md +1 -1
- package/kits/templates/system-fragments/session.md +6 -2
- package/package.json +4 -2
- package/MIGRATION-0.5.0.md +0 -378
- package/ROLE_ACCESS_CONTROL.md +0 -174
- package/dist/channels/contact-bind-code.js +0 -134
- package/dist/channels/wecom-card.js +0 -101
- package/dist/channels/wecom-onboarding.js +0 -82
- package/dist/channels/wecom-state.js +0 -191
|
@@ -25,6 +25,7 @@ import { isCapabilityType, listCapabilityOptions, queryCapabilityTypes, resolveC
|
|
|
25
25
|
import { normalizeCliArgv, parseCliIntent, parseLegacyCliCommand, validateCliArgv, withDefaultRelationContext } from './cli-intent-parser.js';
|
|
26
26
|
import { auditCommandAuthorization, hashArgv } from '../auth/authorization-audit.js';
|
|
27
27
|
import { authorizeOperation, buildAuthSubject } from '../auth/auth-gateway.js';
|
|
28
|
+
import { evaluateRoleOperationCapability } from '../auth/operation-authorizer.js';
|
|
28
29
|
import { splitConfigBatchGetArgv } from '../../config/config-batch-get.js';
|
|
29
30
|
import { chatmodeFieldForPeer, resolveChatModeForField } from '../message/peer-mode.js';
|
|
30
31
|
import { PUBLIC_PERMISSION_MODES } from '../permission/mode.js';
|
|
@@ -37,6 +38,7 @@ import { dispatchToMentionMode } from '../../config/mention-mode.js';
|
|
|
37
38
|
import { menuFailure, menuSuccess, normalizeMenuError, validateConfigWriteScope, validateMenuRequest } from './menu-protocol.js';
|
|
38
39
|
import { roleMenuAction, roleMenuOperation, roleMenuOptions, roleMenuQuery, roleMenuUpdate, } from './role-menu.js';
|
|
39
40
|
import { handleConnectMenu, connectMenuOperation } from './connect-menu.js';
|
|
41
|
+
import { groupMenuAuthorizationArgs, groupMenuIntent, handleGroupMenu, } from './group-menu.js';
|
|
40
42
|
/**
|
|
41
43
|
* 获取 baseagent CLI 的版本号(claude/gemini/codex)。
|
|
42
44
|
* 失败返回 null(命令不存在或执行失败)。
|
|
@@ -67,6 +69,43 @@ const FILE_HASH_MAX_SIZE = 2 * 1024 * 1024;
|
|
|
67
69
|
const FILE_LIST_DEFAULT_LIMIT = 500;
|
|
68
70
|
const FILE_LIST_MAX_LIMIT = 1000;
|
|
69
71
|
const topicForkTargetsInFlight = new Set();
|
|
72
|
+
const GROUP_RULES_RESUME_UNSUPPORTED_NOTE = '群规则加载策略已保存;当前会话使用的 baseagent 不支持在恢复会话中更新群规则上下文。请新建或清空该群会话后生效。';
|
|
73
|
+
function appendGroupRulesCompatibilityNote(execution, command, session) {
|
|
74
|
+
if (!('data' in execution)
|
|
75
|
+
|| execution.data?.exitCode !== 0
|
|
76
|
+
|| command?.kind !== 'field'
|
|
77
|
+
|| (command.subcommand !== 'set' && command.subcommand !== 'unset')
|
|
78
|
+
|| command.field !== 'groupRules.mode'
|
|
79
|
+
|| !session?.baseagent
|
|
80
|
+
|| session.baseagent === 'claude'
|
|
81
|
+
|| session.baseagent === 'codex') {
|
|
82
|
+
return execution;
|
|
83
|
+
}
|
|
84
|
+
const note = `${GROUP_RULES_RESUME_UNSUPPORTED_NOTE}(当前:${session.baseagent})`;
|
|
85
|
+
const stdout = String(execution.data.stdout ?? '');
|
|
86
|
+
if (command.format === 'json') {
|
|
87
|
+
try {
|
|
88
|
+
const payload = JSON.parse(stdout);
|
|
89
|
+
if (payload && typeof payload === 'object' && !Array.isArray(payload)) {
|
|
90
|
+
return {
|
|
91
|
+
data: {
|
|
92
|
+
...execution.data,
|
|
93
|
+
stdout: `${JSON.stringify({ ...payload, note }, null, 2)}\n`,
|
|
94
|
+
},
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
catch {
|
|
99
|
+
// Fall through to a text suffix if a future CLI returns non-JSON output.
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
return {
|
|
103
|
+
data: {
|
|
104
|
+
...execution.data,
|
|
105
|
+
stdout: stdout.trimEnd() ? `${stdout.trimEnd()}\n${note}\n` : `${note}\n`,
|
|
106
|
+
},
|
|
107
|
+
};
|
|
108
|
+
}
|
|
70
109
|
function validateCapabilityScope(args) {
|
|
71
110
|
const scope = args?.scope ?? 'project';
|
|
72
111
|
return scope === 'project' ? null : { error: 'MVP 只支持 scope=project', code: 'INVALID_SCOPE' };
|
|
@@ -180,9 +219,9 @@ async function resolveTopicForkSource(sessionManager, channel, channelId, source
|
|
|
180
219
|
* 沿用 /file 的安全校验链:拒绝 `..` 穿越、realpathSync 后验证落点。
|
|
181
220
|
* 成功返回 `{ realPath, projectPath, stat }`;失败返回 `{ error, code }`。
|
|
182
221
|
*/
|
|
183
|
-
function resolveMenuFilePath(input,
|
|
184
|
-
if (!
|
|
185
|
-
return { error: '
|
|
222
|
+
function resolveMenuFilePath(input, projectPath, role, expectType) {
|
|
223
|
+
if (!projectPath)
|
|
224
|
+
return { error: '无法解析当前 agent projectPath', code: 'NO_PROJECT' };
|
|
186
225
|
const raw = (input ?? '').toString().trim();
|
|
187
226
|
if (!raw)
|
|
188
227
|
return { error: '缺少 path 参数', code: 'MISSING_VALUE' };
|
|
@@ -191,7 +230,7 @@ function resolveMenuFilePath(input, session, role, expectType) {
|
|
|
191
230
|
return { error: '不支持 .. 路径穿越', code: 'NO_PERMISSION' };
|
|
192
231
|
}
|
|
193
232
|
// 相对路径基于 projectPath 解析;绝对路径原样
|
|
194
|
-
const resolved = path.isAbsolute(raw) ? raw : path.resolve(
|
|
233
|
+
const resolved = path.isAbsolute(raw) ? raw : path.resolve(projectPath, raw);
|
|
195
234
|
if (!fs.existsSync(resolved)) {
|
|
196
235
|
return { error: '文件不存在', code: 'NOT_FOUND' };
|
|
197
236
|
}
|
|
@@ -199,7 +238,7 @@ function resolveMenuFilePath(input, session, role, expectType) {
|
|
|
199
238
|
let realProjectPath;
|
|
200
239
|
try {
|
|
201
240
|
realPath = fs.realpathSync(resolved);
|
|
202
|
-
realProjectPath = fs.realpathSync(
|
|
241
|
+
realProjectPath = fs.realpathSync(projectPath);
|
|
203
242
|
}
|
|
204
243
|
catch {
|
|
205
244
|
return { error: '文件不存在', code: 'NOT_FOUND' };
|
|
@@ -825,6 +864,8 @@ function buildMenuIntent(verb, cmdBase, args, action, value, fromControlChannel
|
|
|
825
864
|
});
|
|
826
865
|
const modelScope = args?.scope === 'agent' ? 'agent' : 'relation';
|
|
827
866
|
if (verb === 'query') {
|
|
867
|
+
if (cmdBase === '/file')
|
|
868
|
+
return intent('file.fetch', 'filesystem', { filePath: args?.path });
|
|
828
869
|
if (cmdBase === '/model')
|
|
829
870
|
return intent('model.current', modelScope);
|
|
830
871
|
if (cmdBase === '/effort')
|
|
@@ -864,9 +905,9 @@ function buildMenuIntent(verb, cmdBase, args, action, value, fromControlChannel
|
|
|
864
905
|
if (verb === 'action') {
|
|
865
906
|
if (cmdBase === '/file') {
|
|
866
907
|
if (action === 'list')
|
|
867
|
-
return intent('file.list', 'filesystem');
|
|
908
|
+
return intent('file.list', 'filesystem', { filePath: args?.path });
|
|
868
909
|
if (action === 'fetch')
|
|
869
|
-
return intent('file.fetch', 'filesystem');
|
|
910
|
+
return intent('file.fetch', 'filesystem', { filePath: args?.path });
|
|
870
911
|
}
|
|
871
912
|
if (cmdBase === '/gateway')
|
|
872
913
|
return intent('gateway.write', 'process', { action });
|
|
@@ -964,13 +1005,14 @@ async function authorizeMenuIntent(params) {
|
|
|
964
1005
|
intent.source = source;
|
|
965
1006
|
const decision = await authorizeOperation({ source, intent, subject });
|
|
966
1007
|
if (!decision.allow) {
|
|
967
|
-
if (intent.operation.startsWith('role.')) {
|
|
1008
|
+
if (intent.operation.startsWith('role.') || intent.operation.startsWith('connect.')) {
|
|
1009
|
+
const namespace = intent.operation.split('.', 1)[0];
|
|
968
1010
|
return {
|
|
969
1011
|
error: decision.reason,
|
|
970
1012
|
code: 'PERMISSION_DENIED',
|
|
971
1013
|
data: {
|
|
972
1014
|
$schema_version: 1,
|
|
973
|
-
kind:
|
|
1015
|
+
kind: `${namespace}_permission_denied`,
|
|
974
1016
|
self: subject.selfAid ?? null,
|
|
975
1017
|
actorRole: subject.role,
|
|
976
1018
|
operation: intent.operation,
|
|
@@ -1042,6 +1084,57 @@ async function authorizeRoleMenu(owner, params) {
|
|
|
1042
1084
|
return denied;
|
|
1043
1085
|
return { context: buildRoleMenuContext(owner, params.channel, subject) };
|
|
1044
1086
|
}
|
|
1087
|
+
async function executeGroupMenu(owner, params) {
|
|
1088
|
+
let menuIntent;
|
|
1089
|
+
try {
|
|
1090
|
+
menuIntent = groupMenuIntent(params.kind, params.args);
|
|
1091
|
+
}
|
|
1092
|
+
catch (error) {
|
|
1093
|
+
return menuResultFailure(error);
|
|
1094
|
+
}
|
|
1095
|
+
const authorizationArgs = groupMenuAuthorizationArgs(params.args, params.subject.selfAid, menuIntent.scope === 'relation' ? params.subject.peerKey : undefined);
|
|
1096
|
+
const authorizationSubject = params.subject.isDaemonOwner && params.subject.role !== 'owner'
|
|
1097
|
+
? { ...params.subject, role: 'owner' }
|
|
1098
|
+
: params.subject;
|
|
1099
|
+
const denied = await authorizeMenuIntent.call(owner, {
|
|
1100
|
+
intent: {
|
|
1101
|
+
operation: menuIntent.operation,
|
|
1102
|
+
scope: menuIntent.scope,
|
|
1103
|
+
source: params.source,
|
|
1104
|
+
args: authorizationArgs,
|
|
1105
|
+
},
|
|
1106
|
+
identity: params.identity,
|
|
1107
|
+
subject: authorizationSubject,
|
|
1108
|
+
session: params.session,
|
|
1109
|
+
explicitChatType: params.explicitChatType,
|
|
1110
|
+
channel: params.channel,
|
|
1111
|
+
channelId: params.channelId,
|
|
1112
|
+
userId: params.userId,
|
|
1113
|
+
fromControlChannel: params.fromControlChannel,
|
|
1114
|
+
source: params.source,
|
|
1115
|
+
});
|
|
1116
|
+
if (denied)
|
|
1117
|
+
return { ...denied, code: 'PERMISSION_DENIED' };
|
|
1118
|
+
try {
|
|
1119
|
+
const currentGroupId = params.session?.chatType === 'group'
|
|
1120
|
+
? String(params.session.metadata?.groupId || params.channelId)
|
|
1121
|
+
: undefined;
|
|
1122
|
+
const roleContext = buildRoleMenuContext(owner, params.channel, params.subject);
|
|
1123
|
+
return {
|
|
1124
|
+
data: await handleGroupMenu({ kind: params.kind, args: params.args, value: params.value }, {
|
|
1125
|
+
self: params.subject.selfAid ?? roleContext.self,
|
|
1126
|
+
role: params.subject.role,
|
|
1127
|
+
isDaemonOwner: params.subject.isDaemonOwner,
|
|
1128
|
+
currentGroupId,
|
|
1129
|
+
baseagent: params.session?.baseagent,
|
|
1130
|
+
roleContext,
|
|
1131
|
+
}),
|
|
1132
|
+
};
|
|
1133
|
+
}
|
|
1134
|
+
catch (error) {
|
|
1135
|
+
return menuResultFailure(error);
|
|
1136
|
+
}
|
|
1137
|
+
}
|
|
1045
1138
|
function ecwebErr(id, name, code, message) {
|
|
1046
1139
|
return menuFailure({ id, ...(name ? { name } : {}) }, normalizeMenuError({ code, message }));
|
|
1047
1140
|
}
|
|
@@ -1107,14 +1200,59 @@ export function validateScheduleParams(scheduleType, scheduleValue) {
|
|
|
1107
1200
|
}
|
|
1108
1201
|
/**
|
|
1109
1202
|
* 返回结构化命令菜单(供 menu.query 使用)
|
|
1110
|
-
* owner
|
|
1203
|
+
* owner-only 及尚未迁移的命令保留角色语义;已迁移的命令按 operation capability 展示。
|
|
1111
1204
|
*/
|
|
1112
|
-
export function getMenuItems(role, chatType = 'private', scope = 'agent') {
|
|
1205
|
+
export function getMenuItems(role, chatType = 'private', scope = 'agent', authSubject) {
|
|
1113
1206
|
const isOwner = role === 'owner';
|
|
1114
1207
|
const isAdmin = isManagementRole(role);
|
|
1115
1208
|
const isControlScope = scope === 'control';
|
|
1116
1209
|
const canReadTopic = role !== 'none';
|
|
1117
1210
|
const items = [];
|
|
1211
|
+
const normalizedChatType = chatType === 'group' ? 'group' : 'private';
|
|
1212
|
+
const canOperation = (operation, operationScope) => {
|
|
1213
|
+
if (authSubject) {
|
|
1214
|
+
const relationArgs = operationScope === 'relation'
|
|
1215
|
+
? buildRelationIntentArgs({ selfAid: authSubject.selfAid, peerKey: authSubject.peerKey })
|
|
1216
|
+
: {};
|
|
1217
|
+
return authorizeOperation({
|
|
1218
|
+
source: 'menu',
|
|
1219
|
+
intent: { operation, scope: operationScope, source: 'menu', args: relationArgs },
|
|
1220
|
+
subject: authSubject,
|
|
1221
|
+
audit: false,
|
|
1222
|
+
}).allow;
|
|
1223
|
+
}
|
|
1224
|
+
return evaluateRoleOperationCapability({
|
|
1225
|
+
role,
|
|
1226
|
+
operation,
|
|
1227
|
+
scope: operationScope,
|
|
1228
|
+
chatType: normalizedChatType,
|
|
1229
|
+
fromControlChannel: isControlScope,
|
|
1230
|
+
}).allow;
|
|
1231
|
+
};
|
|
1232
|
+
const canUseModel = canOperation('model.use', 'relation');
|
|
1233
|
+
const canSetEffort = canOperation('model.effort', 'relation');
|
|
1234
|
+
const canSetChatmode = canOperation('chatmode.update', 'relation');
|
|
1235
|
+
const canSetMentionmode = normalizedChatType === 'group' && canOperation('mentionmode.update', 'relation');
|
|
1236
|
+
const canReadPermission = canOperation('permission.current', 'relation');
|
|
1237
|
+
const canUseFile = canOperation('file.fetch', 'filesystem') || canOperation('file.list', 'filesystem');
|
|
1238
|
+
const canManageProcess = authSubject ? authSubject.isDaemonOwner : isOwner;
|
|
1239
|
+
const configurableSettings = [
|
|
1240
|
+
...(canUseModel ? [{ cmd: '/model', label: '切换模型', desc: '切换当前 Agent 使用的模型版本', next: { type: 'select', dynamic: true } }] : []),
|
|
1241
|
+
...(canSetEffort ? [{ cmd: '/effort', label: '切换推理强度', desc: '调整模型推理深度,影响响应速度与质量', next: { type: 'select', items: [
|
|
1242
|
+
{ value: 'low', label: 'Low' },
|
|
1243
|
+
{ value: 'medium', label: 'Medium' },
|
|
1244
|
+
{ value: 'high', label: 'High' },
|
|
1245
|
+
{ value: 'max', label: 'Max' },
|
|
1246
|
+
] } }] : []),
|
|
1247
|
+
...(canSetChatmode ? [{ cmd: '/chatmode', label: '切换会话模式', desc: '控制 Agent 主动性(被动响应或主动推进)', next: { type: 'select', items: [
|
|
1248
|
+
{ value: 'interactive', label: '交互模式', desc: '仅在收到消息时响应' },
|
|
1249
|
+
{ value: 'proactive', label: '主动模式', desc: 'Agent 可主动推进任务' },
|
|
1250
|
+
] } }] : []),
|
|
1251
|
+
...(canSetMentionmode ? [{ cmd: '/mentionmode', label: '切换 @ 处理模式', desc: '控制群聊消息过滤(仅@提及或全部响应)', next: { type: 'select', items: [
|
|
1252
|
+
{ value: 'mention-only', label: '@ 提及', desc: '仅在被 @ 提及时响应' },
|
|
1253
|
+
{ value: 'disabled', label: '全部响应', desc: '响应群内所有消息' },
|
|
1254
|
+
] } }] : []),
|
|
1255
|
+
];
|
|
1118
1256
|
if (!isAdmin && chatType === 'group') {
|
|
1119
1257
|
return [
|
|
1120
1258
|
...(canReadTopic ? [{
|
|
@@ -1123,11 +1261,20 @@ export function getMenuItems(role, chatType = 'private', scope = 'agent') {
|
|
|
1123
1261
|
{ cmd: '/topic', label: '话题管理', desc: '查看当前聊天的话题会话', next: { type: 'select', dynamic: true } },
|
|
1124
1262
|
]
|
|
1125
1263
|
}] : []),
|
|
1264
|
+
...(configurableSettings.length > 0 ? [{
|
|
1265
|
+
group: 'Agent 与模型',
|
|
1266
|
+
commands: configurableSettings,
|
|
1267
|
+
}] : []),
|
|
1268
|
+
...(canReadPermission ? [{
|
|
1269
|
+
group: '权限管理',
|
|
1270
|
+
commands: [{ cmd: '/perm', label: '查看权限模式', desc: '权限模式由当前角色定义决定' }],
|
|
1271
|
+
}] : []),
|
|
1126
1272
|
{
|
|
1127
1273
|
group: '其他',
|
|
1128
1274
|
commands: [
|
|
1129
1275
|
{ cmd: '/status', label: '显示会话状态' },
|
|
1130
1276
|
{ cmd: '/check', label: '检查 EvolAgent 实例健康' },
|
|
1277
|
+
...(canUseFile ? [{ cmd: '/file', label: '发送项目内文件', desc: '将项目目录内的文件发送给用户' }] : []),
|
|
1131
1278
|
{ cmd: '/help', label: '显示帮助信息' },
|
|
1132
1279
|
]
|
|
1133
1280
|
}
|
|
@@ -1148,73 +1295,52 @@ export function getMenuItems(role, chatType = 'private', scope = 'agent') {
|
|
|
1148
1295
|
] : []),
|
|
1149
1296
|
]
|
|
1150
1297
|
});
|
|
1151
|
-
if (isAdmin) {
|
|
1298
|
+
if (isAdmin || configurableSettings.length > 0) {
|
|
1152
1299
|
items.push({
|
|
1153
1300
|
group: 'Agent 与模型',
|
|
1154
1301
|
commands: [
|
|
1155
|
-
{ cmd: '/baseagent', label: '切换 Agent 后端', desc: '切换 Agent 当前 Baseagent', next: { type: 'select', dynamic: true } },
|
|
1156
|
-
|
|
1157
|
-
{ cmd: '/
|
|
1158
|
-
{ value: 'low', label: 'Low' },
|
|
1159
|
-
{ value: 'medium', label: 'Medium' },
|
|
1160
|
-
{ value: 'high', label: 'High' },
|
|
1161
|
-
{ value: 'max', label: 'Max' },
|
|
1162
|
-
] } },
|
|
1163
|
-
{ cmd: '/chatmode', label: '切换会话模式', desc: '控制 Agent 主动性(被动响应或主动推进)', next: { type: 'select', items: [
|
|
1164
|
-
{ value: 'interactive', label: '交互模式', desc: '仅在收到消息时响应' },
|
|
1165
|
-
{ value: 'proactive', label: '主动模式', desc: 'Agent 可主动推进任务' },
|
|
1166
|
-
] } },
|
|
1167
|
-
{ cmd: '/mentionmode', label: '切换 @ 处理模式', desc: '控制群聊消息过滤(仅@提及或全部响应)', next: { type: 'select', items: [
|
|
1168
|
-
{ value: 'mention-only', label: '@ 提及', desc: '仅在被 @ 提及时响应' },
|
|
1169
|
-
{ value: 'disabled', label: '全部响应', desc: '响应群内所有消息' },
|
|
1170
|
-
] } },
|
|
1171
|
-
{ cmd: '/capability', label: '能力开关管理', desc: '查看并管理 Skills / MCP / Plugins' },
|
|
1302
|
+
...(isAdmin ? [{ cmd: '/baseagent', label: '切换 Agent 后端', desc: '切换 Agent 当前 Baseagent', next: { type: 'select', dynamic: true } }] : []),
|
|
1303
|
+
...configurableSettings,
|
|
1304
|
+
...(isAdmin ? [{ cmd: '/capability', label: '能力开关管理', desc: '查看并管理 Skills / MCP / Plugins' }] : []),
|
|
1172
1305
|
]
|
|
1173
1306
|
});
|
|
1307
|
+
}
|
|
1308
|
+
if (canReadPermission) {
|
|
1174
1309
|
items.push({
|
|
1175
1310
|
group: '权限管理',
|
|
1176
1311
|
commands: [
|
|
1177
1312
|
{ cmd: '/perm', label: '查看权限模式', desc: '权限模式由当前角色定义决定' },
|
|
1178
1313
|
]
|
|
1179
1314
|
});
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1315
|
+
}
|
|
1316
|
+
items.push({
|
|
1317
|
+
group: isAdmin ? '运维' : '其他',
|
|
1318
|
+
commands: [
|
|
1319
|
+
{ cmd: '/status', label: '显示会话状态', desc: isAdmin ? '查看当前会话、项目、Agent 的详细状态' : '查看当前会话的基本状态' },
|
|
1320
|
+
...(isAdmin ? [{ cmd: '/stop', label: '中断当前任务', desc: '立即中断正在执行的 Agent 任务' }] : []),
|
|
1321
|
+
{ cmd: '/check', label: '检查 EvolAgent 实例健康', desc: isAdmin ? '检查各 EvolAgent 实例、后端与消息通道的健康状态' : '检查 EvolAgent 实例与消息通道健康状态' },
|
|
1322
|
+
...(isOwner && !isControlScope ? [{
|
|
1323
|
+
cmd: '/observable',
|
|
1324
|
+
label: '观察者模式',
|
|
1325
|
+
desc: '向 owners 转发 Agent 入站和出站消息',
|
|
1326
|
+
next: { type: 'select', items: [
|
|
1327
|
+
{ value: 'true', label: '开启' },
|
|
1328
|
+
{ value: 'false', label: '关闭' },
|
|
1329
|
+
] },
|
|
1330
|
+
}] : []),
|
|
1331
|
+
...(isAdmin ? [{ cmd: '/activity', label: '控制中间输出显示', desc: '设置工具调用过程的可见范围', next: { type: 'select', items: [
|
|
1196
1332
|
{ value: 'all', label: '全部显示', desc: '所有用户均可见中间输出' },
|
|
1197
1333
|
{ value: 'text', label: '仅文字进展', desc: '显示文字进展,隐藏工具活动' },
|
|
1198
1334
|
{ value: 'none', label: '不显示', desc: '关闭所有中间输出' },
|
|
1199
|
-
] } },
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
}
|
|
1209
|
-
else {
|
|
1210
|
-
items.push({
|
|
1211
|
-
group: '其他',
|
|
1212
|
-
commands: [
|
|
1213
|
-
{ cmd: '/status', label: '显示会话状态', desc: '查看当前会话的基本状态' },
|
|
1214
|
-
{ cmd: '/check', label: '检查 EvolAgent 实例健康', desc: '检查 EvolAgent 实例与消息通道健康状态' },
|
|
1215
|
-
]
|
|
1216
|
-
});
|
|
1217
|
-
}
|
|
1335
|
+
] } }] : []),
|
|
1336
|
+
...(isControlScope && canManageProcess && canOperation('system.restart', 'process') ? [
|
|
1337
|
+
{ cmd: '/restart', label: '重启服务', desc: '重启整个 EvolCore 服务进程' },
|
|
1338
|
+
] : []),
|
|
1339
|
+
...(canUseFile ? [
|
|
1340
|
+
{ cmd: '/file', label: '发送项目内文件', desc: '将项目目录内的文件发送给用户' },
|
|
1341
|
+
] : []),
|
|
1342
|
+
]
|
|
1343
|
+
});
|
|
1218
1344
|
items.push({
|
|
1219
1345
|
group: '帮助',
|
|
1220
1346
|
commands: [
|
|
@@ -1263,12 +1389,34 @@ export async function getSubMenuItems(cmd, channel, channelId, userId, args, ove
|
|
|
1263
1389
|
if ('error' in result)
|
|
1264
1390
|
throw result.error;
|
|
1265
1391
|
const opts = result.data?.options ?? [];
|
|
1266
|
-
return opts.map((opt) => ({
|
|
1392
|
+
return opts.map((opt) => ({
|
|
1393
|
+
value: opt.option,
|
|
1394
|
+
label: opt.label,
|
|
1395
|
+
desc: opt.description,
|
|
1396
|
+
...(typeof opt.selected === 'boolean' ? { selected: opt.selected } : {}),
|
|
1397
|
+
}));
|
|
1267
1398
|
}
|
|
1268
1399
|
catch (error) {
|
|
1269
1400
|
throw menuResultFailure(error);
|
|
1270
1401
|
}
|
|
1271
1402
|
}
|
|
1403
|
+
if (cmdBase0 === '/group') {
|
|
1404
|
+
const result = await executeGroupMenu(this, {
|
|
1405
|
+
kind: 'options', args,
|
|
1406
|
+
identity: subject.identity,
|
|
1407
|
+
subject,
|
|
1408
|
+
session,
|
|
1409
|
+
explicitChatType,
|
|
1410
|
+
channel,
|
|
1411
|
+
channelId,
|
|
1412
|
+
userId,
|
|
1413
|
+
fromControlChannel,
|
|
1414
|
+
source,
|
|
1415
|
+
});
|
|
1416
|
+
if ('error' in result)
|
|
1417
|
+
throw { code: result.code, message: result.error, data: result.data };
|
|
1418
|
+
return result.data;
|
|
1419
|
+
}
|
|
1272
1420
|
// ── /agent list(只读) ──
|
|
1273
1421
|
// 控制 channel:验 evolcore.owners,返回全量;agent channel:放行但仅返回自身单条。
|
|
1274
1422
|
if (cmd === '/agent') {
|
|
@@ -1633,6 +1781,12 @@ export async function execMenuQuery(cmd, channel, channelId, userId, args, expli
|
|
|
1633
1781
|
return menuResultFailure(error);
|
|
1634
1782
|
}
|
|
1635
1783
|
}
|
|
1784
|
+
if (cmdBase === '/group') {
|
|
1785
|
+
return await executeGroupMenu(this, {
|
|
1786
|
+
kind: 'query', args, identity, subject, session,
|
|
1787
|
+
explicitChatType, channel, channelId, userId, fromControlChannel, source,
|
|
1788
|
+
});
|
|
1789
|
+
}
|
|
1636
1790
|
const authDenied = await authorizeMenuIntent.call(this, {
|
|
1637
1791
|
intent: buildMenuIntent('query', cmdBase, args, undefined, undefined, fromControlChannel),
|
|
1638
1792
|
identity,
|
|
@@ -2056,13 +2210,11 @@ export async function execMenuQuery(cmd, channel, channelId, userId, args, expli
|
|
|
2056
2210
|
return { data };
|
|
2057
2211
|
}
|
|
2058
2212
|
// ── name=file:文件元信息(§5.1) ──
|
|
2059
|
-
//
|
|
2213
|
+
// operation 权限已在入口统一判定;项目外文件仍仅 owner 可取。
|
|
2060
2214
|
if (cmdBase === '/file') {
|
|
2061
2215
|
const role = identity.role;
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
}
|
|
2065
|
-
const resolved = resolveMenuFilePath(args?.path, session, role, 'file');
|
|
2216
|
+
const projectPath = session?.projectPath ?? evolagent?.projectPath ?? this.getOwningAgent?.(channel)?.projectPath;
|
|
2217
|
+
const resolved = resolveMenuFilePath(args?.path, projectPath, role, 'file');
|
|
2066
2218
|
if ('error' in resolved)
|
|
2067
2219
|
return resolved;
|
|
2068
2220
|
const { realPath, stat } = resolved;
|
|
@@ -2153,6 +2305,12 @@ export async function execMenuUpdate(cmd, value, channel, channelId, userId, ove
|
|
|
2153
2305
|
return menuResultFailure(error);
|
|
2154
2306
|
}
|
|
2155
2307
|
}
|
|
2308
|
+
if (cmdBase === '/group') {
|
|
2309
|
+
return await executeGroupMenu(this, {
|
|
2310
|
+
kind: 'update', args, value: arg, identity, subject, session,
|
|
2311
|
+
channel, channelId, userId, fromControlChannel, source,
|
|
2312
|
+
});
|
|
2313
|
+
}
|
|
2156
2314
|
const authDenied = await authorizeMenuIntent.call(this, {
|
|
2157
2315
|
intent: buildMenuIntent('update', cmdBase, args, undefined, arg, fromControlChannel),
|
|
2158
2316
|
identity,
|
|
@@ -2492,7 +2650,7 @@ export async function execMenuUpdate(cmd, value, channel, channelId, userId, ove
|
|
|
2492
2650
|
return { error: `不支持 update: ${cmdBase}`, code: 'NOT_SUPPORTED' };
|
|
2493
2651
|
}
|
|
2494
2652
|
/** menu.action — 触发动词。 */
|
|
2495
|
-
export async function execMenuAction(cmd, action, args, channel, channelId, userId, overrideIdentity, explicitChatType, requestId, fromControlChannel = false, authSubject, source = 'menu') {
|
|
2653
|
+
export async function execMenuAction(cmd, action, args, channel, channelId, userId, overrideIdentity, explicitChatType, requestId, fromControlChannel = false, authSubject, source = 'menu', transportReplyContext, deferSideEffect) {
|
|
2496
2654
|
const cmdBase = cmd.trim().split(' ')[0];
|
|
2497
2655
|
if (!cmdBase)
|
|
2498
2656
|
return { error: '缺少命令', code: 'MISSING_CMD' };
|
|
@@ -2586,7 +2744,7 @@ export async function execMenuAction(cmd, action, args, channel, channelId, user
|
|
|
2586
2744
|
return await gatewaySyncEnv(args);
|
|
2587
2745
|
return { error: `不支持 gateway action: ${action}`, code: 'NOT_SUPPORTED' };
|
|
2588
2746
|
}
|
|
2589
|
-
const { session } = await this.loadMenuContext(channel, channelId);
|
|
2747
|
+
const { session, evolagent } = await this.loadMenuContext(channel, channelId);
|
|
2590
2748
|
const identity = overrideIdentity ?? this.sessionManager.resolveIdentity(channel, userId, ...menuIdentityArgs(session));
|
|
2591
2749
|
const isMenuAdmin = isManagementRole(identity.role);
|
|
2592
2750
|
// ── /agent action ──
|
|
@@ -3175,6 +3333,7 @@ export async function execMenuAction(cmd, action, args, channel, channelId, user
|
|
|
3175
3333
|
if (!argvValidation.ok)
|
|
3176
3334
|
return { error: argvValidation.reason, code: 'INVALID_ARGUMENT' };
|
|
3177
3335
|
let argv = argvValidation.argv;
|
|
3336
|
+
const requestedArgv = [...argvValidation.argv];
|
|
3178
3337
|
argv = normalizeCliArgv(argv);
|
|
3179
3338
|
const cliAgent = this.getOwningAgent?.(channel);
|
|
3180
3339
|
const cliSelfAid = cliAgent?.aid;
|
|
@@ -3204,6 +3363,7 @@ export async function execMenuAction(cmd, action, args, channel, channelId, user
|
|
|
3204
3363
|
scope: parsed.intent.scope, dangerous: true, actorId: userId,
|
|
3205
3364
|
channel, channelId, role: identity.role, decision: 'deny', code: 'NOT_ALLOWED',
|
|
3206
3365
|
reason: 'Unrecognized CLI command', taskId: requestId, argvHash: hashArgv(intentArgv),
|
|
3366
|
+
name: 'cli', action: 'exec', args: { argv: [...requestedArgv] },
|
|
3207
3367
|
});
|
|
3208
3368
|
return { error: 'CLI command is not allowed', code: 'NOT_ALLOWED' };
|
|
3209
3369
|
}
|
|
@@ -3242,14 +3402,21 @@ export async function execMenuAction(cmd, action, args, channel, channelId, user
|
|
|
3242
3402
|
resolvedConfigCommand: parsed.resolvedConfigCommand,
|
|
3243
3403
|
allowExplicitRelationTarget: true,
|
|
3244
3404
|
auditAllowed: false,
|
|
3245
|
-
auditMetadata: {
|
|
3405
|
+
auditMetadata: {
|
|
3406
|
+
taskId: requestId,
|
|
3407
|
+
argvHash: hashArgv(authArgv),
|
|
3408
|
+
name: 'cli',
|
|
3409
|
+
action: 'exec',
|
|
3410
|
+
args: { argv: [...requestedArgv] },
|
|
3411
|
+
},
|
|
3246
3412
|
});
|
|
3247
3413
|
if (!decision.allow) {
|
|
3248
3414
|
return { error: decision.reason, code: decision.code };
|
|
3249
3415
|
}
|
|
3250
3416
|
authorizedCommands.push({ parsed, decision });
|
|
3251
3417
|
}
|
|
3252
|
-
const
|
|
3418
|
+
const rawExecution = await this.execCliPassthrough(argv, subject.role);
|
|
3419
|
+
const execution = appendGroupRulesCompatibilityNote(rawExecution, parsedCommands[0]?.resolvedConfigCommand, session);
|
|
3253
3420
|
const executionData = 'data' in execution ? execution.data : undefined;
|
|
3254
3421
|
for (const { parsed, decision } of authorizedCommands) {
|
|
3255
3422
|
await auditCommandAuthorization({
|
|
@@ -3259,6 +3426,8 @@ export async function execMenuAction(cmd, action, args, channel, channelId, user
|
|
|
3259
3426
|
channel, channelId, role: identity.role, isDaemonOwner,
|
|
3260
3427
|
fromControlChannel: fromControlChannel ?? false, decision: 'allow',
|
|
3261
3428
|
matchedRule: decision.command?.matchedRule, taskId: requestId, argvHash: hashArgv(argv),
|
|
3429
|
+
name: 'cli', action: 'exec',
|
|
3430
|
+
args: { argv: [...requestedArgv] },
|
|
3262
3431
|
durationMs: executionData?.durationMs, exitCode: executionData?.exitCode,
|
|
3263
3432
|
});
|
|
3264
3433
|
}
|
|
@@ -3269,13 +3438,10 @@ export async function execMenuAction(cmd, action, args, channel, channelId, user
|
|
|
3269
3438
|
// fetch 文件作为独立 result.file 消息异步发回;把请求 id 作为 correlationId 透传,
|
|
3270
3439
|
// 客户端用它把异步到达的文件消息对回这次 fetch 点击。
|
|
3271
3440
|
if (cmdBase === '/file') {
|
|
3272
|
-
|
|
3273
|
-
if (!isManagementRole(identity.role)) {
|
|
3274
|
-
return { error: '无权限', code: 'NO_PERMISSION' };
|
|
3275
|
-
}
|
|
3441
|
+
const projectPath = session?.projectPath ?? evolagent?.projectPath ?? this.getOwningAgent?.(channel)?.projectPath;
|
|
3276
3442
|
if (action === 'list') {
|
|
3277
3443
|
const dirArg = (args?.path ?? '.').toString().trim() || '.';
|
|
3278
|
-
const resolved = resolveMenuFilePath(dirArg,
|
|
3444
|
+
const resolved = resolveMenuFilePath(dirArg, projectPath, identity.role, 'directory');
|
|
3279
3445
|
if ('error' in resolved)
|
|
3280
3446
|
return resolved;
|
|
3281
3447
|
const offset = parseFileListOffset(args?.offset);
|
|
@@ -3288,7 +3454,7 @@ export async function execMenuAction(cmd, action, args, channel, channelId, user
|
|
|
3288
3454
|
}
|
|
3289
3455
|
if (action !== 'fetch')
|
|
3290
3456
|
return { error: `不支持的 file action: ${action}`, code: 'NOT_SUPPORTED' };
|
|
3291
|
-
const resolved = resolveMenuFilePath(args?.path,
|
|
3457
|
+
const resolved = resolveMenuFilePath(args?.path, projectPath, identity.role, 'file');
|
|
3292
3458
|
if ('error' in resolved)
|
|
3293
3459
|
return resolved;
|
|
3294
3460
|
const { realPath, stat } = resolved;
|
|
@@ -3300,9 +3466,26 @@ export async function execMenuAction(cmd, action, args, channel, channelId, user
|
|
|
3300
3466
|
return { error: '通道不存在', code: 'EXEC_FAILED' };
|
|
3301
3467
|
if (!adapter.capabilities?.file)
|
|
3302
3468
|
return { error: '通道不支持文件发送', code: 'NOT_SUPPORTED' };
|
|
3303
|
-
|
|
3304
|
-
|
|
3469
|
+
const sessionReplyContext = session ? this.getReplyContext(session) : undefined;
|
|
3470
|
+
const replyCtx = transportReplyContext
|
|
3471
|
+
? {
|
|
3472
|
+
...(sessionReplyContext ?? {}),
|
|
3473
|
+
...transportReplyContext,
|
|
3474
|
+
metadata: {
|
|
3475
|
+
...(sessionReplyContext?.metadata ?? {}),
|
|
3476
|
+
...(transportReplyContext.metadata ?? {}),
|
|
3477
|
+
},
|
|
3478
|
+
}
|
|
3479
|
+
: sessionReplyContext;
|
|
3480
|
+
const sendFile = async () => {
|
|
3305
3481
|
await adapter.send(buildEnvelope({ channel: adapter.channelName, channelId, replyContext: replyCtx }), { kind: 'result.file', filePath: realPath, correlationId: requestId });
|
|
3482
|
+
};
|
|
3483
|
+
if (deferSideEffect) {
|
|
3484
|
+
deferSideEffect(sendFile);
|
|
3485
|
+
return { data: { accepted: true } };
|
|
3486
|
+
}
|
|
3487
|
+
try {
|
|
3488
|
+
await sendFile();
|
|
3306
3489
|
return { data: { accepted: true } };
|
|
3307
3490
|
}
|
|
3308
3491
|
catch (e) {
|
|
@@ -3319,6 +3502,7 @@ const SYSTEM_CONTROL_NAME_MAP = {
|
|
|
3319
3502
|
observable: '/observable',
|
|
3320
3503
|
agent: '/agent', trigger: '/trigger', file: '/file', gateway: '/gateway',
|
|
3321
3504
|
config: '/config', capability: '/capability', role: '/role',
|
|
3505
|
+
group: '/group', connect: '/connect',
|
|
3322
3506
|
};
|
|
3323
3507
|
function isProcessLevelMenu(name, cmd) {
|
|
3324
3508
|
return name === 'system' || name === 'agent' || name === 'gateway' || name === 'config'
|
|
@@ -3337,9 +3521,9 @@ async function execMenuForSystemControl(payload, context) {
|
|
|
3337
3521
|
const isRoleRequest = name === 'role' || payload?.cmd === '/role';
|
|
3338
3522
|
const isConnectRequest = name === 'connect' || payload?.cmd === '/connect';
|
|
3339
3523
|
const requiresAgentTarget = isRoleRequest || isConnectRequest || [
|
|
3340
|
-
'observable', 'baseagent', 'model', 'effort', 'trigger', 'capability',
|
|
3524
|
+
'observable', 'baseagent', 'model', 'effort', 'trigger', 'capability', 'group',
|
|
3341
3525
|
].includes(name)
|
|
3342
|
-
|| ['/observable', '/baseagent', '/model', '/effort', '/trigger', '/capability'].includes(payload?.cmd);
|
|
3526
|
+
|| ['/observable', '/baseagent', '/model', '/effort', '/trigger', '/capability', '/group'].includes(payload?.cmd);
|
|
3343
3527
|
const target = isProcessLevel
|
|
3344
3528
|
? { channel: SYSTEM_CONTROL_CHANNEL }
|
|
3345
3529
|
: resolveExternalMenuAgentChannel(this, payload, SYSTEM_CONTROL_CHANNEL, requiresAgentTarget);
|
|
@@ -3364,7 +3548,7 @@ async function execMenuForSystemControl(payload, context) {
|
|
|
3364
3548
|
try {
|
|
3365
3549
|
switch (payload?.type) {
|
|
3366
3550
|
case 'menu.list':
|
|
3367
|
-
return menuSuccess({ id }, this.getMenuItems(trustedIdentity.role, 'private', 'control'));
|
|
3551
|
+
return menuSuccess({ id }, this.getMenuItems(trustedIdentity.role, 'private', 'control', trustedSubject));
|
|
3368
3552
|
case 'menu.query': {
|
|
3369
3553
|
if (!cmd)
|
|
3370
3554
|
return ecwebErr(id, name, 'MISSING_CMD', '缺少 name/cmd');
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { createHash } from 'crypto';
|
|
2
2
|
export const MENU_REQUEST_TYPES = new Set([
|
|
3
|
+
'menu.token.request',
|
|
3
4
|
'menu.list',
|
|
4
5
|
'menu.query',
|
|
5
6
|
'menu.options',
|
|
@@ -12,6 +13,7 @@ const EXPLICIT_SCOPE_UPDATE_NAMES = new Set([
|
|
|
12
13
|
'chatmode',
|
|
13
14
|
'dispatch',
|
|
14
15
|
'mentionmode',
|
|
16
|
+
'group',
|
|
15
17
|
'permission',
|
|
16
18
|
]);
|
|
17
19
|
const EXPLICIT_SCOPE_UPDATE_COMMANDS = new Set([
|
|
@@ -20,6 +22,7 @@ const EXPLICIT_SCOPE_UPDATE_COMMANDS = new Set([
|
|
|
20
22
|
'/chatmode',
|
|
21
23
|
'/dispatch',
|
|
22
24
|
'/mentionmode',
|
|
25
|
+
'/group',
|
|
23
26
|
'/perm',
|
|
24
27
|
]);
|
|
25
28
|
function firstCommandToken(value) {
|
|
@@ -78,6 +81,8 @@ export function validateMenuRequest(request) {
|
|
|
78
81
|
if (!MENU_REQUEST_TYPES.has(type)) {
|
|
79
82
|
return { code: 'METHOD_NOT_FOUND', message: `Unknown menu request type: ${type}` };
|
|
80
83
|
}
|
|
84
|
+
if (type === 'menu.token.request')
|
|
85
|
+
return null;
|
|
81
86
|
const hasName = typeof request.name === 'string' && request.name.trim().length > 0;
|
|
82
87
|
const hasCompatCmd = typeof request.cmd === 'string' && request.cmd.trim().length > 0;
|
|
83
88
|
if (type !== 'menu.list' && !hasName && !hasCompatCmd) {
|
|
@@ -144,8 +149,9 @@ const INVALID_ARGUMENT_CODES = new Set([
|
|
|
144
149
|
const STABLE_CODES = new Set([
|
|
145
150
|
'ROLE_ACCESS_DENIED', 'PERMISSION_DENIED', 'UNAUTHORIZED', 'FORBIDDEN',
|
|
146
151
|
'INVALID_ARGUMENT', 'NOT_ALLOWED', 'NOT_SUPPORTED', 'UNSUPPORTED',
|
|
147
|
-
'METHOD_NOT_FOUND', 'NOT_FOUND', 'CONFLICT', 'EXECUTION_TIMEOUT',
|
|
148
|
-
'TEMPORARILY_UNAVAILABLE', 'INTERNAL_ERROR', 'MISSING_SCOPE',
|
|
152
|
+
'METHOD_NOT_FOUND', 'NOT_FOUND', 'CONFLICT', 'EXPIRED', 'EXECUTION_TIMEOUT',
|
|
153
|
+
'TEMPORARILY_UNAVAILABLE', 'INTERNAL_ERROR', 'MISSING_SCOPE', 'UPGRADE_REQUIRED',
|
|
154
|
+
'MENU_TOKEN_REQUIRED', 'MENU_TOKEN_REJECTED', 'MENU_TOKEN_ENCRYPTION_REQUIRED',
|
|
149
155
|
]);
|
|
150
156
|
export function normalizeMenuError(error) {
|
|
151
157
|
const source = error && typeof error === 'object' ? error : {};
|