evolcore 0.0.11 → 0.0.13

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.
Files changed (164) hide show
  1. package/CHANGELOG.md +44 -0
  2. package/bin/codex-managed-hook.mjs +80 -0
  3. package/bin/ec-safe-output.js +26 -7
  4. package/dist/agents/claude-runner.js +86 -7
  5. package/dist/agents/codex-app-server-client.js +174 -20
  6. package/dist/agents/codex-runner.js +375 -92
  7. package/dist/agents/ecagent-runner.js +39 -4
  8. package/dist/agents/gemini-runner.js +1 -0
  9. package/dist/aun/aid/client.js +11 -21
  10. package/dist/aun/aid/control-aid.js +36 -13
  11. package/dist/aun/aid/managed-operation.js +107 -0
  12. package/dist/aun/aid/store.js +13 -7
  13. package/dist/aun/group-fs-download.js +56 -0
  14. package/dist/aun/group-identity.js +1 -2
  15. package/dist/aun/msg/group.js +91 -44
  16. package/dist/aun/msg/index.js +1 -2
  17. package/dist/aun/msg/managed-operation.js +865 -0
  18. package/dist/aun/msg/mention-schema.js +20 -0
  19. package/dist/aun/msg/p2p.js +7 -0
  20. package/dist/aun/msg/upload.js +16 -1
  21. package/dist/aun/outbox.js +31 -2
  22. package/dist/aun/rpc/client.js +66 -0
  23. package/dist/aun/rpc/index.js +1 -2
  24. package/dist/aun/storage/{upload.js → client.js} +11 -2
  25. package/dist/aun/storage/index.js +1 -2
  26. package/dist/channels/aun.js +655 -240
  27. package/dist/channels/daemon.js +13 -0
  28. package/dist/cli/agent-command.js +56 -8
  29. package/dist/cli/agent.js +108 -31
  30. package/dist/cli/aun-commands.js +270 -69
  31. package/dist/cli/bench.js +12 -3
  32. package/dist/cli/cli-argv.js +10 -0
  33. package/dist/cli/config.js +9 -17
  34. package/dist/cli/ctl-command.js +1 -1
  35. package/dist/cli/daemon-commands.js +70 -55
  36. package/dist/cli/fs-command.js +79 -10
  37. package/dist/cli/handoff-command.js +3 -0
  38. package/dist/cli/index.js +30 -2
  39. package/dist/cli/init.js +77 -8
  40. package/dist/cli/managed-command-guard.js +41 -0
  41. package/dist/cli/model.js +11 -4
  42. package/dist/cli/queue-command.js +53 -1
  43. package/dist/cli/response.js +71 -0
  44. package/dist/cli/restart-monitor.js +7 -32
  45. package/dist/cli/trigger-command.js +11 -0
  46. package/dist/config/access-policy.js +1 -2
  47. package/dist/config/builtin-roles.js +9 -0
  48. package/dist/config/config-manager.js +21 -1
  49. package/dist/config/contact-book-store.js +11 -3
  50. package/dist/config/contact-request-service.js +161 -15
  51. package/dist/config/mention-mode.js +8 -24
  52. package/dist/config/peer-role-resolver.js +6 -2
  53. package/dist/config/role-migration-startup.js +53 -0
  54. package/dist/config/role-store.js +1 -1
  55. package/dist/core/auth/agent-delegation.js +1 -2
  56. package/dist/core/auth/auth-gateway.js +29 -1
  57. package/dist/core/auth/authorization-audit.js +15 -0
  58. package/dist/core/auth/operation-authorizer.js +31 -7
  59. package/dist/core/auth/operation-catalog.js +196 -25
  60. package/dist/core/bootstrap-messages.js +25 -0
  61. package/dist/core/bootstrap-service.js +64 -4
  62. package/dist/core/capability/providers/claude-capability-provider.js +9 -31
  63. package/dist/core/capability/providers/codex-capability-provider.js +4 -15
  64. package/dist/core/capability/skill-discovery.js +55 -0
  65. package/dist/core/channel-loader.js +1 -1
  66. package/dist/core/command/cli-intent-parser.js +9 -0
  67. package/dist/core/command/command-handler.js +650 -42
  68. package/dist/core/command/connect-menu.js +15 -0
  69. package/dist/core/command/group-menu.js +4 -4
  70. package/dist/core/command/menu-catalog.js +370 -0
  71. package/dist/core/command/menu-handler.js +230 -51
  72. package/dist/core/command/menu-protocol.js +94 -2
  73. package/dist/core/command/slash-gate.js +5 -2
  74. package/dist/core/command/slash-handler.js +99 -31
  75. package/dist/core/daemon-file-cache.js +2 -1
  76. package/dist/core/data-migration.js +9 -2
  77. package/dist/core/event-catalog.js +6 -0
  78. package/dist/core/evolagent.js +4 -2
  79. package/dist/core/handoff/dispatcher.js +5 -2
  80. package/dist/core/handoff/runtime.js +157 -22
  81. package/dist/core/handoff/store.js +29 -1
  82. package/dist/core/handoff/types.js +1 -0
  83. package/dist/core/interaction-router.js +10 -0
  84. package/dist/core/message/im-renderer.js +19 -0
  85. package/dist/core/message/logical-queue-bridge.js +1 -0
  86. package/dist/core/message/mention-schema.js +126 -0
  87. package/dist/core/message/message-bridge.js +298 -88
  88. package/dist/core/message/message-log.js +3 -0
  89. package/dist/core/message/message-queue.js +268 -39
  90. package/dist/core/message/message-utils.js +81 -4
  91. package/dist/core/message/response-engine.js +161 -77
  92. package/dist/core/message/stream-debouncer.js +10 -2
  93. package/dist/core/permission/approval-gateway.js +1 -0
  94. package/dist/core/permission/ec-command-parser.js +243 -18
  95. package/dist/core/permission/index.js +1 -1
  96. package/dist/core/permission/mode.js +15 -0
  97. package/dist/core/permission/readonly-shell-query.js +148 -8
  98. package/dist/core/permission/sandbox-runtime.js +43 -1
  99. package/dist/core/permission/tool-policy.js +322 -57
  100. package/dist/core/protected-paths.js +114 -4
  101. package/dist/core/session/session-manager.js +42 -4
  102. package/dist/core/session/session-mapper.js +20 -1
  103. package/dist/eck/detect.js +18 -1
  104. package/dist/eck/group-rules-sync.js +15 -44
  105. package/dist/eck/manifest-engine.js +39 -13
  106. package/dist/index.js +541 -180
  107. package/dist/ipc.js +232 -10
  108. package/dist/paths.js +3 -0
  109. package/dist/response-system/coordinator.js +6 -4
  110. package/dist/response-system/engines/v1/proactive-flow.js +26 -8
  111. package/dist/response-system/modes/single-session/index.js +14 -1
  112. package/dist/response-system/selector.js +6 -5
  113. package/dist/trigger/anomaly-store.js +4 -0
  114. package/dist/trigger/feedback.js +64 -11
  115. package/dist/trigger/manager.js +21 -1
  116. package/dist/trigger/parser.js +14 -2
  117. package/dist/trigger/scheduler.js +1 -0
  118. package/dist/trigger/script-executor.js +1 -0
  119. package/dist/trigger/validation.js +67 -6
  120. package/dist/utils/codex-app-server-registry.js +90 -0
  121. package/dist/utils/codex-cli.js +5 -1
  122. package/dist/utils/cross-platform.js +15 -0
  123. package/dist/utils/ecweb-supervisor.js +332 -0
  124. package/dist/utils/error-utils.js +10 -0
  125. package/dist/utils/npm-ops.js +5 -12
  126. package/dist/utils/tool-summary.js +11 -0
  127. package/kits/docs/channels/aun.md +1 -1
  128. package/kits/docs/context-assembly.md +2 -2
  129. package/kits/docs/evolcore/agent.md +11 -6
  130. package/kits/docs/evolcore/aid.md +14 -0
  131. package/kits/docs/evolcore/trigger.md +17 -1
  132. package/kits/docs/prompt-loading-architecture.md +1 -2
  133. package/kits/docs/venues/group.md +1 -1
  134. package/kits/eck_manifest.json +0 -12
  135. package/kits/migrations/migrate-contact-book-v2.mjs +1 -2
  136. package/kits/rules/04-relation.md +1 -1
  137. package/kits/rules/05-venue.md +2 -2
  138. package/kits/schemas/role-config.schema.1.json +1 -0
  139. package/kits/schemas/single-session.schema.1.json +3 -3
  140. package/kits/schemas/single-session.schema.2.json +3 -3
  141. package/kits/templates/roles/admin.json +27 -0
  142. package/kits/templates/roles/member.json +34 -1
  143. package/kits/templates/roles/visitor.json +35 -3
  144. package/kits/templates/system-fragments/bootstrap.md +1 -1
  145. package/kits/templates/system-fragments/channel.md +1 -1
  146. package/kits/templates/system-fragments/session.md +1 -1
  147. package/package.json +2 -2
  148. package/dist/aun/msg/payload-type.js +0 -27
  149. package/dist/aun/rpc/caller.js +0 -42
  150. package/dist/aun/rpc/connection.js +0 -25
  151. package/dist/aun/storage/manage.js +0 -10
  152. package/dist/config/access-policy-domain.js +0 -18
  153. package/dist/config/config-batch-get.js +0 -11
  154. package/dist/config/owner-policy.js +0 -4
  155. package/dist/config/role-config-v4-startup.js +0 -32
  156. package/dist/config/role-config-v5-startup.js +0 -27
  157. package/dist/core/auth/trigger-authorization.js +0 -15
  158. package/dist/core/command/evol-menu-version-gate.js +0 -39
  159. package/dist/core/interaction-registration.js +0 -10
  160. package/dist/core/permission/execution-sandbox.js +0 -16
  161. package/dist/core/relation/peer-key.js +0 -1
  162. package/dist/core/session/session-key.js +0 -24
  163. package/dist/eck/baseagent-caps.js +0 -18
  164. package/dist/eck/rules-loader.js +0 -28
@@ -6,7 +6,7 @@ import { hasModelSwitcher } from '../../agents/runner-types.js';
6
6
  import { getCodexEfforts } from '../../agents/codex-runner.js';
7
7
  import { execCodexCliSync, resolveCodexCliPath } from '../../utils/codex-cli.js';
8
8
  import { resolvePaths, getPackageRoot, daemonControlDir } from '../../paths.js';
9
- import { buildEnvelope } from '../message/message-utils.js';
9
+ import { buildEnvelope, isDeliveryTarget, isDeliveryTargetForChannel, replyContextFromSession } from '../message/message-utils.js';
10
10
  import path from 'path';
11
11
  import fs from 'fs';
12
12
  import os from 'os';
@@ -15,7 +15,7 @@ import { execFileSync } from 'child_process';
15
15
  import { CronExpressionParser } from 'cron-parser';
16
16
  import { parseDuration } from '../../trigger/parser.js';
17
17
  import { checkLatestVersion, getLocalVersion, isLinkedInstall, compareVersions, resolveGlobalPkg } from '../../utils/npm-ops.js';
18
- import { commandExists } from '../../utils/cross-platform.js';
18
+ import { commandExists, spawnDetachedNode } from '../../utils/cross-platform.js';
19
19
  import { 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';
@@ -28,7 +28,7 @@ import { normalizeCliArgv, parseCliIntent, parseLegacyCliCommand, validateCliArg
28
28
  import { auditCommandAuthorization, hashArgv } from '../auth/authorization-audit.js';
29
29
  import { authorizeOperation, buildAuthSubject } from '../auth/auth-gateway.js';
30
30
  import { evaluateRoleOperationCapability } from '../auth/operation-authorizer.js';
31
- import { splitConfigBatchGetArgv } from '../../config/config-batch-get.js';
31
+ import { splitConfigBatchGetArgv } from '../../cli/cli-argv.js';
32
32
  import { chatmodeFieldForPeer, resolveChatModeForField } from '../message/peer-mode.js';
33
33
  import { PUBLIC_PERMISSION_MODES } from '../permission/mode.js';
34
34
  import { resolveRuntimePermissionMode, resolveRuntimeStringField, validateRuntimeStringFieldOverride, } from '../role/runtime-policy.js';
@@ -36,11 +36,12 @@ import { isManagementRole } from '../../config/builtin-roles.js';
36
36
  import { SYSTEM_CONTROL_CHANNEL } from '../system-channels.js';
37
37
  import { logger } from '../../utils/logger.js';
38
38
  import { shouldSuppressRealRestart } from '../../utils/restart-safety.js';
39
- import { dispatchToMentionMode } from '../../config/mention-mode.js';
40
- import { menuFailure, menuSuccess, normalizeMenuError, validateConfigWriteScope, validateMenuRequest } from './menu-protocol.js';
39
+ import { AGENT_DELEGATION_TOKEN_ENV } from '../auth/agent-delegation.js';
40
+ import { evolMenuResponseTransportMetadata, menuFailure, menuCommandForName, menuSuccess, normalizeMenuError, validateConfigWriteScope, validateMenuRequest, } from './menu-protocol.js';
41
41
  import { roleMenuAction, roleMenuOperation, roleMenuOptions, roleMenuQuery, roleMenuUpdate, } from './role-menu.js';
42
42
  import { handleConnectMenu, connectMenuOperation } from './connect-menu.js';
43
43
  import { groupMenuAuthorizationArgs, groupMenuIntent, handleGroupMenu, } from './group-menu.js';
44
+ import { getMenuCatalog, execMenuValueBatch, resolveExplicitMenuExecutionContext, validateMenuCatalogArgs, validateMenuValueBatchArgs } from './menu-catalog.js';
44
45
  /**
45
46
  * 获取 baseagent CLI 的版本号(claude/gemini/codex)。
46
47
  * 失败返回 null(命令不存在或执行失败)。
@@ -60,6 +61,7 @@ function getBaseagentVersion(cmd) {
60
61
  encoding: 'utf-8',
61
62
  timeout: 3000,
62
63
  stdio: ['ignore', 'pipe', 'pipe'],
64
+ windowsHide: process.platform === 'win32',
63
65
  });
64
66
  const versionOutput = String(output).trim();
65
67
  // claude: "2.1.187 (Claude Code)" → 提取 "2.1.187"
@@ -880,7 +882,7 @@ function buildMenuIntent(verb, cmdBase, args, action, value, fromControlChannel
880
882
  if (cmdBase === '/topic')
881
883
  return intent('session.topic.current', 'relation');
882
884
  if (cmdBase === '/baseagent')
883
- return intent('agent.baseagent.current', fromControlChannel ? 'agent' : 'relation');
885
+ return intent('agent.baseagent.current', fromControlChannel || args?.scope === 'agent' ? 'agent' : 'relation');
884
886
  if (cmdBase === '/file')
885
887
  return intent('file.fetch', 'filesystem', { filePath: args?.path });
886
888
  if (cmdBase === '/model')
@@ -938,6 +940,8 @@ function buildMenuIntent(verb, cmdBase, args, action, value, fromControlChannel
938
940
  return intent('permission.current', args?.scope === 'agent' ? 'agent' : 'relation');
939
941
  }
940
942
  if (verb === 'update') {
943
+ if (cmdBase === '/baseagent')
944
+ return intent('agent.baseagent.update', 'agent', { baseagent: value });
941
945
  if (cmdBase === '/model')
942
946
  return intent('model.use', modelScope, { model: value });
943
947
  if (cmdBase === '/effort')
@@ -946,6 +950,10 @@ function buildMenuIntent(verb, cmdBase, args, action, value, fromControlChannel
946
950
  return intent('chatmode.update', args?.scope === 'agent' ? 'agent' : 'relation', { value });
947
951
  if (cmdBase === '/mentionmode')
948
952
  return intent('mentionmode.update', args?.scope === 'agent' ? 'agent' : 'relation', { value });
953
+ if (cmdBase === '/activity')
954
+ return intent('activity.update', args?.scope === 'agent' ? 'agent' : 'relation', { value });
955
+ if (cmdBase === '/observable')
956
+ return intent('observable.update', 'agent', { value });
949
957
  if (cmdBase === '/gateway')
950
958
  return intent('gateway.write', 'process');
951
959
  if (cmdBase === '/config')
@@ -1006,8 +1014,13 @@ function buildRelationIntentArgs(params) {
1006
1014
  return out;
1007
1015
  }
1008
1016
  function buildMenuAuthSubject(owner, params) {
1009
- if (params.subject)
1010
- return params.subject;
1017
+ if (params.subject) {
1018
+ return {
1019
+ ...params.subject,
1020
+ ...(params.requestId && !params.subject.requestId ? { requestId: params.requestId } : {}),
1021
+ ...(params.session?.id && !params.subject.sessionId ? { sessionId: params.session.id } : {}),
1022
+ };
1023
+ }
1011
1024
  const agent = owner.getOwningAgent?.(params.channel);
1012
1025
  const selfAid = agent?.aid || params.session?.selfAID;
1013
1026
  const channelType = owner.resolveChannelType?.(params.channel) || params.session?.channelType;
@@ -1018,6 +1031,8 @@ function buildMenuAuthSubject(owner, params) {
1018
1031
  return buildAuthSubject({
1019
1032
  selfAid,
1020
1033
  actorId: params.userId,
1034
+ requestId: params.requestId,
1035
+ sessionId: params.session?.id,
1021
1036
  channel: params.channel,
1022
1037
  channelType: channelType || params.channel.split('#')[0],
1023
1038
  channelId: params.channelId,
@@ -1089,6 +1104,9 @@ function buildRoleMenuContext(owner, channel, subject) {
1089
1104
  }
1090
1105
  const aunAvailable = (owningAgent?.channelInstanceNames?.() ?? [])
1091
1106
  .some((channelName) => owner.hasRegisteredChannel?.(channelName, 'aun') === true);
1107
+ const aunChannelName = (owningAgent?.channelInstanceNames?.() ?? [])
1108
+ .find((name) => String(name).split('#', 1)[0].toLowerCase() === 'aun');
1109
+ const contactAdapter = aunChannelName ? owner.getAdapter?.(aunChannelName) : undefined;
1092
1110
  return {
1093
1111
  self,
1094
1112
  actorRole: subject.role,
@@ -1097,6 +1115,26 @@ function buildRoleMenuContext(owner, channel, subject) {
1097
1115
  actorAid: subject.actorId,
1098
1116
  chatType: subject.chatType,
1099
1117
  aunAvailable,
1118
+ ...(contactAdapter?.send ? {
1119
+ notifyContactRequestReviewed: async ({ applicantAid, requestId, expiresAt, status }) => {
1120
+ await contactAdapter.send(buildEnvelope({
1121
+ taskId: `contact-request-${requestId}`,
1122
+ channel: contactAdapter.channelName,
1123
+ channelId: applicantAid,
1124
+ agentName: owningAgent?.name ?? '<unknown>',
1125
+ replyContext: { delivery: { chatType: 'private' }, metadata: evolMenuResponseTransportMetadata() },
1126
+ }), {
1127
+ kind: 'custom',
1128
+ channelType: 'aun',
1129
+ payload: {
1130
+ type: 'contact.request.response',
1131
+ status,
1132
+ request_id: requestId,
1133
+ expires_at: expiresAt,
1134
+ },
1135
+ });
1136
+ },
1137
+ } : {}),
1100
1138
  availableBaseagents: Array.from(new Set([
1101
1139
  ...Object.keys(owningAgent?.config?.baseagents ?? {}),
1102
1140
  ...(owningAgent?.baseagent ? [owningAgent.baseagent] : []),
@@ -1285,6 +1323,7 @@ export function getMenuItems(role, chatType = 'private', scope = 'agent', authSu
1285
1323
  fromControlChannel: isControlScope,
1286
1324
  }).allow;
1287
1325
  };
1326
+ const canReadModel = canOperation('model.list', 'relation') || canOperation('model.current', 'relation');
1288
1327
  const canUseModel = canOperation('model.use', 'relation');
1289
1328
  const canSetEffort = canOperation('model.effort', 'relation');
1290
1329
  const canSetChatmode = canOperation('chatmode.update', 'relation');
@@ -1293,7 +1332,12 @@ export function getMenuItems(role, chatType = 'private', scope = 'agent', authSu
1293
1332
  const canUseFile = canOperation('file.fetch', 'filesystem') || canOperation('file.list', 'filesystem');
1294
1333
  const canManageProcess = authSubject ? authSubject.isDaemonOwner : isOwner;
1295
1334
  const configurableSettings = [
1296
- ...(canUseModel ? [{ cmd: '/model', label: '切换模型', desc: '切换当前 Agent 使用的模型版本', next: { type: 'select', dynamic: true } }] : []),
1335
+ ...(canReadModel ? [{
1336
+ cmd: '/model',
1337
+ label: canUseModel ? '切换模型' : '查看模型',
1338
+ desc: canUseModel ? '切换当前 Agent 使用的模型版本' : '查看当前 Agent 使用的模型版本',
1339
+ next: canUseModel ? { type: 'select', dynamic: true } : undefined,
1340
+ }] : []),
1297
1341
  ...(canSetEffort ? [{ cmd: '/effort', label: '切换推理强度', desc: '调整模型推理深度,影响响应速度与质量', next: { type: 'select', items: [
1298
1342
  { value: 'low', label: 'Low' },
1299
1343
  { value: 'medium', label: 'Medium' },
@@ -1729,7 +1773,7 @@ export async function getSubMenuItems(cmd, channel, channelId, userId, args, ove
1729
1773
  });
1730
1774
  if ('error' in target)
1731
1775
  throw { code: target.code, message: target.error };
1732
- const fallback = dispatchToMentionMode(session?.metadata?.dispatchMode) ?? null;
1776
+ const fallback = session?.metadata?.mentionMode ?? null;
1733
1777
  const currentMode = readMenuMentionMode(target, fallback, fallback === null ? null : 'session').value;
1734
1778
  return [
1735
1779
  { value: 'mention-only', label: '@提及时响应', selected: currentMode === 'mention-only' },
@@ -1755,6 +1799,10 @@ export async function execMenuQuery(cmd, channel, channelId, userId, args, expli
1755
1799
  const cmdBase = cmd.trim().split(' ')[0];
1756
1800
  if (!cmdBase)
1757
1801
  return { error: '缺少命令', code: 'MISSING_CMD' };
1802
+ const isRequesterRoleQuery = cmdBase === '/role' && args?.view === 'requester';
1803
+ if (isRequesterRoleQuery && Object.keys(args).some(key => key !== 'view')) {
1804
+ return { error: 'role requester query only accepts args.view', code: 'INVALID_ARGUMENT' };
1805
+ }
1758
1806
  const gated = gateControlScope.call(this, { cmdBase, args, channel, fromControlChannel });
1759
1807
  if (gated)
1760
1808
  return gated;
@@ -1770,6 +1818,18 @@ export async function execMenuQuery(cmd, channel, channelId, userId, args, expli
1770
1818
  fromControlChannel,
1771
1819
  subject: authSubject,
1772
1820
  });
1821
+ if (isRequesterRoleQuery) {
1822
+ if (!subject.allowAccess) {
1823
+ return { error: `Role ${subject.role} is not allowed to access this agent`, code: 'ROLE_ACCESS_DENIED' };
1824
+ }
1825
+ return {
1826
+ data: {
1827
+ roleId: subject.role,
1828
+ source: subject.roleSource,
1829
+ allowAccess: subject.allowAccess,
1830
+ },
1831
+ };
1832
+ }
1773
1833
  if (cmdBase === '/role') {
1774
1834
  const authorized = await authorizeRoleMenu(this, {
1775
1835
  kind: 'query', args, identity, subject, session, channel, channelId,
@@ -2091,8 +2151,7 @@ export async function execMenuQuery(cmd, channel, channelId, userId, args, expli
2091
2151
  });
2092
2152
  if ('error' in target)
2093
2153
  return target;
2094
- // session metadata 是 AUN 协议词汇,翻译成 mentionMode 词汇;evolagent.config 已是 mentionMode 字段
2095
- const fallback = dispatchToMentionMode(session?.metadata?.dispatchMode) ?? null;
2154
+ const fallback = session?.metadata?.mentionMode ?? null;
2096
2155
  const current = readMenuMentionMode(target, fallback, fallback === null ? null : 'session');
2097
2156
  return {
2098
2157
  data: {
@@ -2605,8 +2664,6 @@ export async function execMenuUpdate(cmd, value, channel, channelId, userId, ove
2605
2664
  const newMode = modeMap[arg];
2606
2665
  if (!newMode)
2607
2666
  return { error: `无效模式: ${arg},可选: all / text / none`, code: 'INVALID_VALUE' };
2608
- if (identity.role !== 'owner')
2609
- return { error: '中间输出模式切换仅限 owner', code: 'NO_PERMISSION' };
2610
2667
  const target = resolveMenuActivityTarget.call(this, {
2611
2668
  args,
2612
2669
  session,
@@ -2661,6 +2718,7 @@ export async function execMenuAction(cmd, action, args, channel, channelId, user
2661
2718
  const subject = buildMenuAuthSubject(this, {
2662
2719
  identity: authIdentity,
2663
2720
  session: authSession,
2721
+ requestId,
2664
2722
  explicitChatType,
2665
2723
  channel,
2666
2724
  channelId,
@@ -2878,6 +2936,10 @@ export async function execMenuAction(cmd, action, args, channel, channelId, user
2878
2936
  if (triggerThread !== undefined && triggerThread !== 'per_run' && triggerThread !== 'by_run' && triggerThread !== 'by_trigger') {
2879
2937
  return { error: `无效 triggerThread: ${triggerThread}`, code: 'INVALID_ARGS' };
2880
2938
  }
2939
+ const targetChatType = args.targetChatType;
2940
+ if (targetChatType !== undefined && targetChatType !== 'private' && targetChatType !== 'group') {
2941
+ return { error: `无效 targetChatType: ${targetChatType}`, code: 'INVALID_ARGS' };
2942
+ }
2881
2943
  const parsed = {
2882
2944
  scheduleType: args.scheduleType,
2883
2945
  scheduleValue: String(args.scheduleValue ?? ''),
@@ -2888,6 +2950,7 @@ export async function execMenuAction(cmd, action, args, channel, channelId, user
2888
2950
  name: args.name,
2889
2951
  targetChannel: args.targetChannel,
2890
2952
  targetChannelId: args.targetChannelId,
2953
+ targetChatType,
2891
2954
  targetThreadId: args.targetThreadId,
2892
2955
  agentId: args.agentId,
2893
2956
  model: args.model,
@@ -3264,17 +3327,57 @@ export async function execMenuAction(cmd, action, args, channel, channelId, user
3264
3327
  }
3265
3328
  if (action === 'restart') {
3266
3329
  const suppressRealRestart = shouldSuppressRealRestart();
3330
+ let restartReplyContext;
3331
+ try {
3332
+ restartReplyContext = session
3333
+ ? replyContextFromSession(session, transportReplyContext)
3334
+ : transportReplyContext;
3335
+ }
3336
+ catch (error) {
3337
+ const code = error?.code;
3338
+ if (channel.split('#', 1)[0].toLowerCase() !== 'aun'
3339
+ || (code !== 'AUN_OUTBOUND_ROUTE_REQUIRED' && code !== 'AUN_OUTBOUND_ROUTE_MISMATCH')) {
3340
+ throw error;
3341
+ }
3342
+ return {
3343
+ error: '当前 AUN 会话缺少有效的持久化出站路由,请从可信入站上下文重试',
3344
+ code,
3345
+ };
3346
+ }
3347
+ const restartDelivery = restartReplyContext?.delivery
3348
+ ?? (explicitChatType === 'group'
3349
+ ? { chatType: 'group', groupId: channelId }
3350
+ : explicitChatType === 'private'
3351
+ ? { chatType: 'private' }
3352
+ : undefined);
3353
+ if (channel.split('#', 1)[0].toLowerCase() === 'aun'
3354
+ && !isDeliveryTargetForChannel(restartDelivery, channelId)) {
3355
+ return {
3356
+ error: '无法确定有效的重启通知出站路由,请从可信会话上下文重试',
3357
+ code: isDeliveryTarget(restartDelivery)
3358
+ ? 'AUN_OUTBOUND_ROUTE_MISMATCH'
3359
+ : 'AUN_OUTBOUND_ROUTE_REQUIRED',
3360
+ };
3361
+ }
3267
3362
  if (!suppressRealRestart) {
3268
- const restartInfo = { channel, channelId, timestamp: Date.now() };
3363
+ const restartInfo = {
3364
+ channel,
3365
+ channelId,
3366
+ timestamp: Date.now(),
3367
+ ...(restartDelivery ? { delivery: restartDelivery } : {}),
3368
+ };
3269
3369
  const controlDir = daemonControlDir();
3270
3370
  fs.mkdirSync(controlDir, { recursive: true });
3271
3371
  fs.writeFileSync(path.join(controlDir, 'restart-pending.json'), JSON.stringify(restartInfo));
3272
- const { spawn } = await import('child_process');
3273
- spawn('node', [path.join(getPackageRoot(), 'dist', 'cli', 'index.js'), 'restart-monitor'], {
3274
- detached: true,
3275
- stdio: 'ignore',
3276
- env: { ...process.env, EVOLCORE_HOME: resolvePaths().root }
3277
- }).unref();
3372
+ // Use the canonical CLI restart path so the restart itself performs
3373
+ // the version check and automatic upgrades before replacing the daemon.
3374
+ // Do not inherit managed-session credentials: otherwise cmdRestart can
3375
+ // delegate back to this daemon and recursively enqueue another restart.
3376
+ spawnDetachedNode([path.join(getPackageRoot(), 'dist', 'cli', 'index.js'), 'restart'], {
3377
+ EVOLCORE_HOME: resolvePaths().root,
3378
+ EVOLCORE_SESSION_ID: '',
3379
+ [AGENT_DELEGATION_TOKEN_ENV]: '',
3380
+ });
3278
3381
  }
3279
3382
  else {
3280
3383
  logger.info('[System] Suppressed real menu restart in test runtime');
@@ -3310,7 +3413,7 @@ export async function execMenuAction(cmd, action, args, channel, channelId, user
3310
3413
  data: {
3311
3414
  devMode,
3312
3415
  evolcore: { local: localEvolcore, remote: evolcoreRemote, hasUpdate: cmp(localEvolcore, evolcoreRemote) },
3313
- fastaun: { local: localFastaun, remote: fastaunRemote, hasUpdate: cmp(localFastaun, fastaunRemote) },
3416
+ fastaun: { local: localFastaun, remote: fastaunRemote, hasUpdate: cmp(localFastaun, fastaunRemote), managedBy: 'evolcore' },
3314
3417
  // ecweb 本地版本由 ECWeb 进程自身注入(data.ecwebVersion),此处仅给 remote
3315
3418
  ecweb: { remote: ecwebRemote },
3316
3419
  },
@@ -3340,6 +3443,17 @@ export async function execMenuAction(cmd, action, args, channel, channelId, user
3340
3443
  ? (session?.metadata?.groupId || channelId)
3341
3444
  : userId;
3342
3445
  const cliPeerKey = cliChannelType && cliPeerKeyId ? formatPeerKey(cliChannelType, cliPeerKeyId) : undefined;
3446
+ const subject = buildMenuAuthSubject(this, {
3447
+ identity,
3448
+ session,
3449
+ requestId,
3450
+ explicitChatType,
3451
+ channel,
3452
+ channelId,
3453
+ userId,
3454
+ fromControlChannel: fromControlChannel ?? false,
3455
+ subject: authSubject,
3456
+ });
3343
3457
  if (Array.isArray(args?.argv)) {
3344
3458
  argv = withDefaultRelationContext(argv, { self: cliSelfAid, peer: cliPeerKey });
3345
3459
  }
@@ -3358,6 +3472,8 @@ export async function execMenuAction(cmd, action, args, channel, channelId, user
3358
3472
  await auditCommandAuthorization({
3359
3473
  ts: Date.now(), source: 'menu.cli', operation: parsed.intent.operation,
3360
3474
  scope: parsed.intent.scope, dangerous: true, actorId: userId,
3475
+ requestId, sessionId: subject.sessionId, agentAid: subject.selfAid,
3476
+ permissionMode: subject.permissionMode,
3361
3477
  channel, channelId, role: identity.role, decision: 'deny', code: 'NOT_ALLOWED',
3362
3478
  reason: 'Unrecognized CLI command', taskId: requestId, argvHash: hashArgv(intentArgv),
3363
3479
  name: 'cli', action: 'exec', args: { argv: [...requestedArgv] },
@@ -3366,16 +3482,6 @@ export async function execMenuAction(cmd, action, args, channel, channelId, user
3366
3482
  }
3367
3483
  parsedCommands.push(parsed);
3368
3484
  }
3369
- const subject = buildMenuAuthSubject(this, {
3370
- identity,
3371
- session,
3372
- explicitChatType,
3373
- channel,
3374
- channelId,
3375
- userId,
3376
- fromControlChannel: fromControlChannel ?? false,
3377
- subject: authSubject,
3378
- });
3379
3485
  const selfAid = subject.selfAid;
3380
3486
  const peerKey = subject.peerKey;
3381
3487
  const isDaemonOwner = subject.isDaemonOwner;
@@ -3400,6 +3506,10 @@ export async function execMenuAction(cmd, action, args, channel, channelId, user
3400
3506
  allowExplicitRelationTarget: true,
3401
3507
  auditAllowed: false,
3402
3508
  auditMetadata: {
3509
+ requestId,
3510
+ sessionId: subject.sessionId,
3511
+ agentAid: subject.selfAid,
3512
+ permissionMode: subject.permissionMode,
3403
3513
  taskId: requestId,
3404
3514
  argvHash: hashArgv(authArgv),
3405
3515
  name: 'cli',
@@ -3420,6 +3530,8 @@ export async function execMenuAction(cmd, action, args, channel, channelId, user
3420
3530
  ts: Date.now(), source: 'menu.cli', operation: parsed.intent.operation,
3421
3531
  scope: parsed.intent.scope, dangerous: parsed.intent.dangerous ?? false,
3422
3532
  actorId: userId, selfAid, peerKey,
3533
+ requestId, sessionId: subject.sessionId, agentAid: selfAid,
3534
+ permissionMode: subject.permissionMode,
3423
3535
  channel, channelId, role: identity.role, isDaemonOwner,
3424
3536
  fromControlChannel: fromControlChannel ?? false, decision: 'allow',
3425
3537
  matchedRule: decision.command?.matchedRule, taskId: requestId, argvHash: hashArgv(argv),
@@ -3463,7 +3575,7 @@ export async function execMenuAction(cmd, action, args, channel, channelId, user
3463
3575
  return { error: '通道不存在', code: 'EXEC_FAILED' };
3464
3576
  if (!adapter.capabilities?.file)
3465
3577
  return { error: '通道不支持文件发送', code: 'NOT_SUPPORTED' };
3466
- const sessionReplyContext = session ? this.getReplyContext(session) : undefined;
3578
+ const sessionReplyContext = session ? replyContextFromSession(session, this.getReplyContext(session)) : undefined;
3467
3579
  const replyCtx = transportReplyContext
3468
3580
  ? {
3469
3581
  ...(sessionReplyContext ?? {}),
@@ -3491,16 +3603,6 @@ export async function execMenuAction(cmd, action, args, channel, channelId, user
3491
3603
  }
3492
3604
  return { error: `不支持 action: ${cmdBase}`, code: 'NOT_SUPPORTED' };
3493
3605
  }
3494
- const SYSTEM_CONTROL_NAME_MAP = {
3495
- pwd: '/pwd', session: '/session', baseagent: '/baseagent', model: '/model',
3496
- topic: '/topic',
3497
- effort: '/effort', chatmode: '/chatmode', mentionmode: '/mentionmode',
3498
- permission: '/perm', activity: '/activity', system: '/system',
3499
- observable: '/observable',
3500
- agent: '/agent', trigger: '/trigger', file: '/file', gateway: '/gateway',
3501
- config: '/config', capability: '/capability', role: '/role',
3502
- group: '/group', connect: '/connect',
3503
- };
3504
3606
  function menuCommandToken(cmd) {
3505
3607
  if (typeof cmd !== 'string')
3506
3608
  return undefined;
@@ -3511,6 +3613,13 @@ function isProcessLevelMenu(name, cmd) {
3511
3613
  return name === 'system' || name === 'agent' || name === 'gateway' || name === 'config'
3512
3614
  || cmdToken === '/system' || cmdToken === '/agent' || cmdToken === '/gateway' || cmdToken === '/config';
3513
3615
  }
3616
+ function isProcessContextMenu(payload) {
3617
+ const isCatalog = payload?.type === 'menu.list' && payload?.args?.view === 'catalog';
3618
+ const isValueBatch = payload?.type === 'menu.query'
3619
+ && payload?.name === 'menu'
3620
+ && payload?.args?.view === 'values';
3621
+ return (isCatalog || isValueBatch) && payload?.args?.context?.kind === 'process';
3622
+ }
3514
3623
  async function execMenuForSystemControl(payload, context) {
3515
3624
  const id = payload?.id ?? '';
3516
3625
  const name = payload?.name;
@@ -3531,13 +3640,15 @@ async function execMenuForSystemControl(payload, context) {
3531
3640
  : 'cli 不在控制 channel 范围';
3532
3641
  return { type: 'menu.response', id, name, error: { code: 'NOT_SUPPORTED', message } };
3533
3642
  }
3534
- const isProcessLevel = isProcessLevelMenu(name, payload?.cmd);
3643
+ const isProcessLevel = isProcessLevelMenu(name, payload?.cmd) || isProcessContextMenu(payload);
3644
+ const requestedContextKind = payload?.args?.context?.kind;
3535
3645
  const isRoleRequest = name === 'role' || compatCmd === '/role';
3536
3646
  const isConnectRequest = name === 'connect' || compatCmd === '/connect';
3537
3647
  const requiresAgentTarget = isRoleRequest || isConnectRequest || [
3538
3648
  'observable', 'baseagent', 'model', 'effort', 'trigger', 'capability', 'group',
3539
3649
  ].includes(name)
3540
- || ['/observable', '/baseagent', '/model', '/effort', '/trigger', '/capability', '/group'].includes(compatCmd ?? '');
3650
+ || ['/observable', '/baseagent', '/model', '/effort', '/trigger', '/capability', '/group'].includes(compatCmd ?? '')
3651
+ || (!isProcessLevel && requestedContextKind !== undefined && requestedContextKind !== 'process');
3541
3652
  const target = isProcessLevel
3542
3653
  ? { channel: SYSTEM_CONTROL_CHANNEL }
3543
3654
  : resolveExternalMenuAgentChannel(this, payload, SYSTEM_CONTROL_CHANNEL, requiresAgentTarget);
@@ -3548,6 +3659,7 @@ async function execMenuForSystemControl(payload, context) {
3548
3659
  const trustedSubject = buildAuthSubject({
3549
3660
  selfAid: targetAid,
3550
3661
  actorId: context.actorAid,
3662
+ requestId: id !== undefined && id !== null && String(id) ? String(id) : undefined,
3551
3663
  channel: targetChannel,
3552
3664
  channelType: 'aun',
3553
3665
  channelId: context.actorAid || SYSTEM_CONTROL_CHANNEL,
@@ -3558,21 +3670,71 @@ async function execMenuForSystemControl(payload, context) {
3558
3670
  fromControlChannel: true,
3559
3671
  });
3560
3672
  const trustedIdentity = trustedSubject.identity;
3561
- const cmd = name ? (SYSTEM_CONTROL_NAME_MAP[name] ?? payload.cmd) : payload.cmd;
3673
+ const cmd = name ? (menuCommandForName(name) ?? payload.cmd) : payload.cmd;
3562
3674
  try {
3563
3675
  switch (payload?.type) {
3564
3676
  case 'menu.list':
3677
+ if (payload?.args?.view === 'catalog') {
3678
+ validateMenuCatalogArgs(payload.args);
3679
+ const data = await getMenuCatalog({
3680
+ subject: trustedSubject,
3681
+ channelType: 'aun',
3682
+ channelScope: 'control',
3683
+ channelId: targetChannel,
3684
+ request: payload.args.context,
3685
+ });
3686
+ return menuSuccess({ id, ...(name ? { name } : {}) }, data);
3687
+ }
3688
+ if (payload?.args?.view !== undefined)
3689
+ return ecwebErr(id, name, 'NOT_SUPPORTED', `Unsupported menu.list view: ${String(payload.args.view)}`);
3565
3690
  return menuSuccess({ id }, this.getMenuItems(trustedIdentity.role, 'private', 'control', trustedSubject));
3566
3691
  case 'menu.query': {
3692
+ if (name === 'menu' && payload?.args?.view === 'values') {
3693
+ validateMenuValueBatchArgs(payload.args);
3694
+ const data = await execMenuValueBatch({
3695
+ subject: trustedSubject,
3696
+ channelType: 'aun',
3697
+ channelScope: 'control',
3698
+ channelId: targetChannel,
3699
+ request: payload.args.context,
3700
+ items: payload.args.items,
3701
+ schemaVersion: payload.args.schemaVersion,
3702
+ consistency: payload.args.consistency,
3703
+ execQuery: async (entryCmd, resolvedChannelId, queryArgs, catalogContext) => this.execMenuQuery(entryCmd, targetChannel, resolvedChannelId, catalogContext.subject.actorId, queryArgs, catalogContext.subject.chatType, true, catalogContext.subject.identity, catalogContext.subject, context.source),
3704
+ });
3705
+ return menuSuccess({ id, ...(name ? { name } : {}) }, data);
3706
+ }
3707
+ if (name === 'menu')
3708
+ return ecwebErr(id, name, 'NOT_SUPPORTED', `Unsupported menu.query view: ${String(payload?.args?.view)}`);
3567
3709
  if (!cmd)
3568
3710
  return ecwebErr(id, name, 'MISSING_CMD', '缺少 name/cmd');
3569
- const r = await this.execMenuQuery(cmd, targetChannel, targetChannel, context.actorAid, payload.args, undefined, true, trustedIdentity, trustedSubject, context.source);
3711
+ const hasExplicitContext = !!payload?.args && Object.prototype.hasOwnProperty.call(payload.args, 'context');
3712
+ const execution = await resolveExplicitMenuExecutionContext({
3713
+ subject: trustedSubject,
3714
+ channelType: 'aun',
3715
+ channelScope: 'control',
3716
+ channelId: targetChannel,
3717
+ args: payload.args,
3718
+ });
3719
+ const r = hasExplicitContext
3720
+ ? await this.execMenuQuery(cmd, targetChannel, execution.channelId, execution.subject.actorId, execution.args, execution.subject.chatType, true, execution.subject.identity, execution.subject, context.source)
3721
+ : await this.execMenuQuery(cmd, targetChannel, targetChannel, context.actorAid, payload.args, undefined, true, trustedIdentity, trustedSubject, context.source);
3570
3722
  return ecwebResp(id, name, r);
3571
3723
  }
3572
3724
  case 'menu.options': {
3573
3725
  if (!cmd)
3574
3726
  return ecwebErr(id, name, 'MISSING_CMD', '缺少 name/cmd');
3575
- const data = await this.getSubMenuItems(cmd, targetChannel, targetChannel, context.actorAid, payload.args, trustedIdentity, undefined, true, trustedSubject, context.source) ?? [];
3727
+ const hasExplicitContext = !!payload?.args && Object.prototype.hasOwnProperty.call(payload.args, 'context');
3728
+ const execution = await resolveExplicitMenuExecutionContext({
3729
+ subject: trustedSubject,
3730
+ channelType: 'aun',
3731
+ channelScope: 'control',
3732
+ channelId: targetChannel,
3733
+ args: payload.args,
3734
+ });
3735
+ const data = hasExplicitContext
3736
+ ? await this.getSubMenuItems(cmd, targetChannel, execution.channelId, execution.subject.actorId, execution.args, execution.subject.identity, execution.subject.chatType, true, execution.subject, context.source) ?? []
3737
+ : await this.getSubMenuItems(cmd, targetChannel, targetChannel, context.actorAid, payload.args, trustedIdentity, undefined, true, trustedSubject, context.source) ?? [];
3576
3738
  return menuSuccess({ id, ...(name ? { name } : {}) }, data);
3577
3739
  }
3578
3740
  case 'menu.update': {
@@ -3580,7 +3742,14 @@ async function execMenuForSystemControl(payload, context) {
3580
3742
  return ecwebErr(id, name, 'MISSING_CMD', '缺少 name/cmd');
3581
3743
  if (!payload.value)
3582
3744
  return ecwebErr(id, name, 'MISSING_VALUE', '缺少 value');
3583
- const r = await this.execMenuUpdate(cmd, payload.value, targetChannel, targetChannel, context.actorAid, trustedIdentity, true, payload.args, trustedSubject, context.source);
3745
+ const execution = await resolveExplicitMenuExecutionContext({
3746
+ subject: trustedSubject,
3747
+ channelType: 'aun',
3748
+ channelScope: 'control',
3749
+ channelId: targetChannel,
3750
+ args: payload.args,
3751
+ });
3752
+ const r = await this.execMenuUpdate(cmd, payload.value, targetChannel, execution.channelId, execution.subject.actorId, execution.subject.identity, true, execution.args, execution.subject, context.source);
3584
3753
  return ecwebResp(id, name, r);
3585
3754
  }
3586
3755
  case 'menu.action': {
@@ -3588,7 +3757,17 @@ async function execMenuForSystemControl(payload, context) {
3588
3757
  return ecwebErr(id, name, 'MISSING_CMD', '缺少 name/cmd');
3589
3758
  if (!payload.action)
3590
3759
  return ecwebErr(id, name, 'MISSING_VALUE', '缺少 action');
3591
- const r = await this.execMenuAction(cmd, payload.action, payload.args, targetChannel, targetChannel, context.actorAid, trustedIdentity, undefined, id, true, trustedSubject, context.source);
3760
+ const hasExplicitContext = !!payload?.args && Object.prototype.hasOwnProperty.call(payload.args, 'context');
3761
+ const execution = await resolveExplicitMenuExecutionContext({
3762
+ subject: trustedSubject,
3763
+ channelType: 'aun',
3764
+ channelScope: 'control',
3765
+ channelId: targetChannel,
3766
+ args: payload.args,
3767
+ });
3768
+ const r = hasExplicitContext
3769
+ ? await this.execMenuAction(cmd, payload.action, execution.args, targetChannel, execution.channelId, execution.subject.actorId, execution.subject.identity, execution.subject.chatType, id, true, execution.subject, context.source)
3770
+ : await this.execMenuAction(cmd, payload.action, payload.args, targetChannel, targetChannel, context.actorAid, trustedIdentity, undefined, id, true, trustedSubject, context.source);
3592
3771
  return ecwebResp(id, name, r);
3593
3772
  }
3594
3773
  default:
@@ -3606,7 +3785,7 @@ export async function execMenuForEcweb(payload, trusted) {
3606
3785
  const validationError = validateMenuRequest(payload ?? {});
3607
3786
  if (validationError)
3608
3787
  return menuFailure({ id, ...(name ? { name } : {}) }, validationError);
3609
- const isProcessLevel = isProcessLevelMenu(name, payload?.cmd);
3788
+ const isProcessLevel = isProcessLevelMenu(name, payload?.cmd) || isProcessContextMenu(payload);
3610
3789
  const owners = loadDaemonConfig().owners ?? [];
3611
3790
  if (isProcessLevel && owners.length === 0) {
3612
3791
  return { type: 'menu.response', id, name, error: { code: 'FORBIDDEN', message: '请在 daemon.json 配置 owners 后使用进程级操作' } };