evolcore 0.0.8 → 0.0.9

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 (213) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/README.md +37 -3
  3. package/dist/agents/baseagent.js +111 -19
  4. package/dist/agents/claude-runner.js +30 -2
  5. package/dist/agents/codex-app-server-client.js +31 -0
  6. package/dist/agents/codex-runner.js +3 -0
  7. package/dist/aun/msg/group.js +9 -9
  8. package/dist/aun/msg/p2p.js +11 -6
  9. package/dist/aun/msg/upload.js +43 -29
  10. package/dist/aun/outbox.js +48 -5
  11. package/dist/channels/aun.js +199 -50
  12. package/dist/cli/aun-commands.js +10 -7
  13. package/dist/cli/daemon-commands.js +19 -4
  14. package/dist/cli/index.js +11 -1
  15. package/dist/cli/init.js +103 -4
  16. package/dist/cli/model.js +5 -5
  17. package/dist/config/builtin-role-templates.js +4 -3
  18. package/dist/config/config-field-policy.js +6 -2
  19. package/dist/config/config-manager.js +48 -11
  20. package/dist/config/role-schema.js +18 -3
  21. package/dist/config/role-service.js +3 -3
  22. package/dist/config/role-store.js +4 -3
  23. package/dist/config-store.js +4 -0
  24. package/dist/core/auth/operation-authorizer.js +3 -1
  25. package/dist/core/bootstrap-service.js +0 -12
  26. package/dist/core/channel-loader.js +5 -1
  27. package/dist/core/command/command-handler.js +1 -1
  28. package/dist/core/command/evol-menu-version-gate.js +3 -2
  29. package/dist/core/command/menu-handler.js +33 -22
  30. package/dist/core/command/menu-protocol.js +7 -4
  31. package/dist/core/command/slash-handler.js +4 -4
  32. package/dist/core/inference/text-inference.js +18 -5
  33. package/dist/core/message/message-bridge.js +17 -6
  34. package/dist/core/message/response-engine.js +22 -3
  35. package/dist/core/model/config-scope.js +3 -3
  36. package/dist/core/permission/tool-policy.js +18 -0
  37. package/dist/index.js +43 -26
  38. package/dist/ipc.js +1 -1
  39. package/dist/product.js +1 -0
  40. package/dist/utils/autostart.js +27 -0
  41. package/dist/utils/ecweb-utils.js +15 -2
  42. package/dist/utils/macos-autostart.js +179 -0
  43. package/dist/utils/windows-autostart.js +130 -0
  44. package/ecagent/dist/agent-loop.d.ts +1 -1
  45. package/ecagent/dist/agent-loop.js +1 -1
  46. package/ecagent/dist/agent.d.ts +1 -1
  47. package/ecagent/dist/agent.js +1 -1
  48. package/ecagent/dist/harness/agent-harness.d.ts +1 -1
  49. package/ecagent/dist/harness/compaction/branch-summarization.d.ts +1 -1
  50. package/ecagent/dist/harness/compaction/compaction.d.ts +1 -1
  51. package/ecagent/dist/harness/compaction/utils.d.ts +1 -1
  52. package/ecagent/dist/harness/messages.d.ts +1 -1
  53. package/ecagent/dist/harness/session/session.d.ts +1 -1
  54. package/ecagent/dist/harness/types.d.ts +1 -1
  55. package/ecagent/dist/proxy.d.ts +1 -1
  56. package/ecagent/dist/proxy.js +1 -1
  57. package/ecagent/dist/types.d.ts +1 -1
  58. package/kits/migrations/migrate-role-config-v5.mjs +27 -2
  59. package/kits/schemas/agent-config.schema.10.json +19 -1
  60. package/kits/schemas/daemon.schema.4.json +4 -0
  61. package/kits/schemas/defaults.schema.4.json +15 -1
  62. package/kits/schemas/relation-config.schema.8.json +22 -1
  63. package/kits/schemas/role-config.schema.1.json +2 -2
  64. package/package.json +2 -4
  65. package/ecagent/dist/agent-loop.d.ts.map +0 -1
  66. package/ecagent/dist/agent-loop.js.map +0 -1
  67. package/ecagent/dist/agent.d.ts.map +0 -1
  68. package/ecagent/dist/agent.js.map +0 -1
  69. package/ecagent/dist/harness/agent-harness.d.ts.map +0 -1
  70. package/ecagent/dist/harness/agent-harness.js.map +0 -1
  71. package/ecagent/dist/harness/compaction/branch-summarization.d.ts.map +0 -1
  72. package/ecagent/dist/harness/compaction/branch-summarization.js.map +0 -1
  73. package/ecagent/dist/harness/compaction/compaction.d.ts.map +0 -1
  74. package/ecagent/dist/harness/compaction/compaction.js.map +0 -1
  75. package/ecagent/dist/harness/compaction/utils.d.ts.map +0 -1
  76. package/ecagent/dist/harness/compaction/utils.js.map +0 -1
  77. package/ecagent/dist/harness/env/nodejs.d.ts.map +0 -1
  78. package/ecagent/dist/harness/env/nodejs.js.map +0 -1
  79. package/ecagent/dist/harness/messages.d.ts.map +0 -1
  80. package/ecagent/dist/harness/messages.js.map +0 -1
  81. package/ecagent/dist/harness/prompt-templates.d.ts.map +0 -1
  82. package/ecagent/dist/harness/prompt-templates.js.map +0 -1
  83. package/ecagent/dist/harness/session/jsonl-repo.d.ts.map +0 -1
  84. package/ecagent/dist/harness/session/jsonl-repo.js.map +0 -1
  85. package/ecagent/dist/harness/session/jsonl-storage.d.ts.map +0 -1
  86. package/ecagent/dist/harness/session/jsonl-storage.js.map +0 -1
  87. package/ecagent/dist/harness/session/memory-repo.d.ts.map +0 -1
  88. package/ecagent/dist/harness/session/memory-repo.js.map +0 -1
  89. package/ecagent/dist/harness/session/memory-storage.d.ts.map +0 -1
  90. package/ecagent/dist/harness/session/memory-storage.js.map +0 -1
  91. package/ecagent/dist/harness/session/repo-utils.d.ts.map +0 -1
  92. package/ecagent/dist/harness/session/repo-utils.js.map +0 -1
  93. package/ecagent/dist/harness/session/session.d.ts.map +0 -1
  94. package/ecagent/dist/harness/session/session.js.map +0 -1
  95. package/ecagent/dist/harness/session/uuid.d.ts.map +0 -1
  96. package/ecagent/dist/harness/session/uuid.js.map +0 -1
  97. package/ecagent/dist/harness/skills.d.ts.map +0 -1
  98. package/ecagent/dist/harness/skills.js.map +0 -1
  99. package/ecagent/dist/harness/system-prompt.d.ts.map +0 -1
  100. package/ecagent/dist/harness/system-prompt.js.map +0 -1
  101. package/ecagent/dist/harness/types.d.ts.map +0 -1
  102. package/ecagent/dist/harness/types.js.map +0 -1
  103. package/ecagent/dist/harness/utils/shell-output.d.ts.map +0 -1
  104. package/ecagent/dist/harness/utils/shell-output.js.map +0 -1
  105. package/ecagent/dist/harness/utils/truncate.d.ts.map +0 -1
  106. package/ecagent/dist/harness/utils/truncate.js.map +0 -1
  107. package/ecagent/dist/index.d.ts.map +0 -1
  108. package/ecagent/dist/index.js.map +0 -1
  109. package/ecagent/dist/node.d.ts.map +0 -1
  110. package/ecagent/dist/node.js.map +0 -1
  111. package/ecagent/dist/proxy.d.ts.map +0 -1
  112. package/ecagent/dist/proxy.js.map +0 -1
  113. package/ecagent/dist/runtime/api/lazy.d.ts.map +0 -1
  114. package/ecagent/dist/runtime/api/lazy.js.map +0 -1
  115. package/ecagent/dist/runtime/api/pi-messages.d.ts.map +0 -1
  116. package/ecagent/dist/runtime/api/pi-messages.js.map +0 -1
  117. package/ecagent/dist/runtime/api/pi-messages.lazy.d.ts.map +0 -1
  118. package/ecagent/dist/runtime/api/pi-messages.lazy.js.map +0 -1
  119. package/ecagent/dist/runtime/auth/context.d.ts.map +0 -1
  120. package/ecagent/dist/runtime/auth/context.js.map +0 -1
  121. package/ecagent/dist/runtime/auth/credential-store.d.ts.map +0 -1
  122. package/ecagent/dist/runtime/auth/credential-store.js.map +0 -1
  123. package/ecagent/dist/runtime/auth/helpers.d.ts.map +0 -1
  124. package/ecagent/dist/runtime/auth/helpers.js.map +0 -1
  125. package/ecagent/dist/runtime/auth/resolve.d.ts.map +0 -1
  126. package/ecagent/dist/runtime/auth/resolve.js.map +0 -1
  127. package/ecagent/dist/runtime/auth/types.d.ts.map +0 -1
  128. package/ecagent/dist/runtime/auth/types.js.map +0 -1
  129. package/ecagent/dist/runtime/compat.d.ts.map +0 -1
  130. package/ecagent/dist/runtime/compat.js.map +0 -1
  131. package/ecagent/dist/runtime/index.d.ts.map +0 -1
  132. package/ecagent/dist/runtime/index.js.map +0 -1
  133. package/ecagent/dist/runtime/models.d.ts.map +0 -1
  134. package/ecagent/dist/runtime/models.js.map +0 -1
  135. package/ecagent/dist/runtime/oauth.d.ts.map +0 -1
  136. package/ecagent/dist/runtime/oauth.js.map +0 -1
  137. package/ecagent/dist/runtime/providers/faux.d.ts.map +0 -1
  138. package/ecagent/dist/runtime/providers/faux.js.map +0 -1
  139. package/ecagent/dist/runtime/types.d.ts.map +0 -1
  140. package/ecagent/dist/runtime/types.js.map +0 -1
  141. package/ecagent/dist/runtime/utils/diagnostics.d.ts.map +0 -1
  142. package/ecagent/dist/runtime/utils/diagnostics.js.map +0 -1
  143. package/ecagent/dist/runtime/utils/event-stream.d.ts.map +0 -1
  144. package/ecagent/dist/runtime/utils/event-stream.js.map +0 -1
  145. package/ecagent/dist/runtime/utils/headers.d.ts.map +0 -1
  146. package/ecagent/dist/runtime/utils/headers.js.map +0 -1
  147. package/ecagent/dist/runtime/utils/json-parse.d.ts.map +0 -1
  148. package/ecagent/dist/runtime/utils/json-parse.js.map +0 -1
  149. package/ecagent/dist/runtime/utils/oauth/types.d.ts.map +0 -1
  150. package/ecagent/dist/runtime/utils/oauth/types.js.map +0 -1
  151. package/ecagent/dist/runtime/utils/overflow.d.ts.map +0 -1
  152. package/ecagent/dist/runtime/utils/overflow.js.map +0 -1
  153. package/ecagent/dist/runtime/utils/provider-env.d.ts.map +0 -1
  154. package/ecagent/dist/runtime/utils/provider-env.js.map +0 -1
  155. package/ecagent/dist/runtime/utils/retry.d.ts.map +0 -1
  156. package/ecagent/dist/runtime/utils/retry.js.map +0 -1
  157. package/ecagent/dist/runtime/utils/typebox-helpers.d.ts.map +0 -1
  158. package/ecagent/dist/runtime/utils/typebox-helpers.js.map +0 -1
  159. package/ecagent/dist/runtime/utils/validation.d.ts.map +0 -1
  160. package/ecagent/dist/runtime/utils/validation.js.map +0 -1
  161. package/ecagent/dist/types.d.ts.map +0 -1
  162. package/ecagent/dist/types.js.map +0 -1
  163. package/ecagent/package.json +0 -93
  164. package/ecagent/src/agent-loop.ts +0 -792
  165. package/ecagent/src/agent.ts +0 -575
  166. package/ecagent/src/harness/agent-harness.ts +0 -1029
  167. package/ecagent/src/harness/compaction/branch-summarization.ts +0 -261
  168. package/ecagent/src/harness/compaction/compaction.ts +0 -753
  169. package/ecagent/src/harness/compaction/utils.ts +0 -144
  170. package/ecagent/src/harness/env/nodejs.ts +0 -569
  171. package/ecagent/src/harness/messages.ts +0 -164
  172. package/ecagent/src/harness/prompt-templates.ts +0 -267
  173. package/ecagent/src/harness/session/jsonl-repo.ts +0 -179
  174. package/ecagent/src/harness/session/jsonl-storage.ts +0 -314
  175. package/ecagent/src/harness/session/memory-repo.ts +0 -50
  176. package/ecagent/src/harness/session/memory-storage.ts +0 -133
  177. package/ecagent/src/harness/session/repo-utils.ts +0 -51
  178. package/ecagent/src/harness/session/session.ts +0 -338
  179. package/ecagent/src/harness/session/uuid.ts +0 -54
  180. package/ecagent/src/harness/skills.ts +0 -375
  181. package/ecagent/src/harness/system-prompt.ts +0 -34
  182. package/ecagent/src/harness/types.ts +0 -838
  183. package/ecagent/src/harness/utils/shell-output.ts +0 -135
  184. package/ecagent/src/harness/utils/truncate.ts +0 -344
  185. package/ecagent/src/index.ts +0 -46
  186. package/ecagent/src/node.ts +0 -2
  187. package/ecagent/src/proxy.ts +0 -367
  188. package/ecagent/src/runtime/api/lazy.ts +0 -70
  189. package/ecagent/src/runtime/api/pi-messages.lazy.ts +0 -4
  190. package/ecagent/src/runtime/api/pi-messages.ts +0 -436
  191. package/ecagent/src/runtime/auth/context.ts +0 -45
  192. package/ecagent/src/runtime/auth/credential-store.ts +0 -47
  193. package/ecagent/src/runtime/auth/helpers.ts +0 -46
  194. package/ecagent/src/runtime/auth/resolve.ts +0 -141
  195. package/ecagent/src/runtime/auth/types.ts +0 -182
  196. package/ecagent/src/runtime/compat.ts +0 -158
  197. package/ecagent/src/runtime/index.ts +0 -31
  198. package/ecagent/src/runtime/models.ts +0 -452
  199. package/ecagent/src/runtime/oauth.ts +0 -1
  200. package/ecagent/src/runtime/providers/faux.ts +0 -538
  201. package/ecagent/src/runtime/types.ts +0 -718
  202. package/ecagent/src/runtime/utils/diagnostics.ts +0 -45
  203. package/ecagent/src/runtime/utils/event-stream.ts +0 -88
  204. package/ecagent/src/runtime/utils/headers.ts +0 -18
  205. package/ecagent/src/runtime/utils/json-parse.ts +0 -124
  206. package/ecagent/src/runtime/utils/oauth/types.ts +0 -79
  207. package/ecagent/src/runtime/utils/overflow.ts +0 -165
  208. package/ecagent/src/runtime/utils/provider-env.ts +0 -52
  209. package/ecagent/src/runtime/utils/retry.ts +0 -101
  210. package/ecagent/src/runtime/utils/typebox-helpers.ts +0 -24
  211. package/ecagent/src/runtime/utils/validation.ts +0 -310
  212. package/ecagent/src/types.ts +0 -430
  213. package/ecagent/tsconfig.build.json +0 -32
@@ -27,9 +27,11 @@ export function showActivitiesPolicy(mode, chatType, _identity) {
27
27
  // ── ChannelLoader ──────────────────────────────────────────────────────────
28
28
  export class ChannelLoader {
29
29
  processDebug;
30
+ aunDefaultEncrypt;
30
31
  plugins = new Map();
31
- constructor(processDebug) {
32
+ constructor(processDebug, aunDefaultEncrypt) {
32
33
  this.processDebug = processDebug;
34
+ this.aunDefaultEncrypt = aunDefaultEncrypt;
33
35
  }
34
36
  register(plugin) {
35
37
  if (this.plugins.has(plugin.name)) {
@@ -51,6 +53,7 @@ export class ChannelLoader {
51
53
  agentName: agent.aid,
52
54
  defaultProjectPath: agent.config.projects?.defaultPath ?? process.cwd(),
53
55
  debug: this.processDebug,
56
+ aunDefaultEncrypt: this.aunDefaultEncrypt,
54
57
  };
55
58
  // Build the full list of config instances to create.
56
59
  // AUN is synthesised from agent.aid; any explicit aun entry in channels[] is skipped.
@@ -266,6 +269,7 @@ export function buildReloadHooks(deps) {
266
269
  agentName: agent.aid ?? agent.config?.aid,
267
270
  defaultProjectPath: agent.config?.projects?.defaultPath ?? process.cwd(),
268
271
  debug: channelLoader.processDebug,
272
+ aunDefaultEncrypt: channelLoader.aunDefaultEncrypt,
269
273
  };
270
274
  const plugin = channelLoader.getPlugin(cfgInst.type);
271
275
  if (!plugin)
@@ -372,7 +372,7 @@ export class CommandHandler {
372
372
  const effortDecision = validateRuntimeStringFieldOverride({
373
373
  selfAid: definition.agentAid,
374
374
  role,
375
- field: `baseagents.${base}.${base === 'codex' ? 'reasoning' : 'effort'}`,
375
+ field: `baseagents.${base}.effort`,
376
376
  value: definition.execution.effort,
377
377
  });
378
378
  if (!effortDecision.ok)
@@ -30,9 +30,10 @@ export function isEvolMenuVersionGateEnabled() {
30
30
  export function isAunMenuTokenRequired() {
31
31
  return loadDaemonConfig().aun?.menuTokenRequired === true;
32
32
  }
33
- export function evolMenuResponseTransportMetadata(_requestEncrypted) {
33
+ export function evolMenuResponseTransportMetadata() {
34
34
  return {
35
- encrypted: true,
35
+ // Clear inherited request state so AUNChannel applies daemon aun.defaultEncrypt.
36
+ encrypted: undefined,
36
37
  protectedHeaders: { ec_version: readInstalledEvolcoreVersion() },
37
38
  };
38
39
  }
@@ -9,6 +9,7 @@ import { resolvePaths, getPackageRoot, daemonControlDir } from '../../paths.js';
9
9
  import { buildEnvelope } from '../message/message-utils.js';
10
10
  import path from 'path';
11
11
  import fs from 'fs';
12
+ import os from 'os';
12
13
  import crypto from 'crypto';
13
14
  import { execFileSync } from 'child_process';
14
15
  import { CronExpressionParser } from 'cron-parser';
@@ -452,9 +453,6 @@ function menuConfigSource(fieldPath, sel) {
452
453
  return null;
453
454
  }
454
455
  function menuModelConfigFieldPath(target) {
455
- if (target.field === 'effort' && target.baseagent === 'codex') {
456
- return `baseagents.${target.baseagent}.reasoning`;
457
- }
458
456
  return target.fieldPath;
459
457
  }
460
458
  function menuChatmodeScope(args) {
@@ -1881,7 +1879,6 @@ export async function execMenuQuery(cmd, channel, channelId, userId, args, expli
1881
1879
  const target = resolveCapabilityTarget.call(this, { channel, args, session, evolagent, fromControlChannel });
1882
1880
  if ('error' in target)
1883
1881
  return target;
1884
- const identity = this.sessionManager.resolveIdentity(channel, userId, ...menuIdentityArgs(session));
1885
1882
  const data = queryCapabilityTypes(target.ctx, target.config, type);
1886
1883
  return { data: sanitizeCapabilityQueryForRole(data, identity.role) };
1887
1884
  }
@@ -2179,11 +2176,13 @@ export async function execMenuQuery(cmd, channel, channelId, userId, args, expli
2179
2176
  if (!subject.isDaemonOwner) {
2180
2177
  return { error: '操作需要 owner 权限', code: 'FORBIDDEN' };
2181
2178
  }
2182
- const owningAgent = this.getOwningAgent(channel);
2183
2179
  const data = {
2184
2180
  aid: loadDaemonConfig().aid ?? null,
2185
2181
  pid: process.pid,
2186
- node: process.version,
2182
+ nodejs: process.version,
2183
+ hostname: os.hostname(),
2184
+ platform: process.platform,
2185
+ arch: process.arch,
2187
2186
  uptime: Math.floor(process.uptime()),
2188
2187
  };
2189
2188
  try {
@@ -2204,14 +2203,6 @@ export async function execMenuQuery(cmd, channel, channelId, userId, args, expli
2204
2203
  const ecwebPkg = resolveGlobalPkg(WEB_PACKAGE_NAME);
2205
2204
  if (ecwebPkg?.version)
2206
2205
  data.ecwebVersion = ecwebPkg.version;
2207
- const channels = owningAgent?.channelInstanceNames?.() ?? [];
2208
- if (channels.length) {
2209
- // 将 channelKey 字符串(如 "feishu#aid#name")解析为对象 { type, instName }
2210
- data.channels = channels.map((key) => {
2211
- const parts = key.split('#');
2212
- return { type: parts[0], instName: parts[2] || parts[0] };
2213
- });
2214
- }
2215
2206
  // 收集主机级可用的所有 baseagent 类型(检测已安装的 CLI + 版本)
2216
2207
  const baseagents = [];
2217
2208
  for (const cmd of ['claude', 'gemini', 'codex']) {
@@ -2260,7 +2251,9 @@ export async function execMenuUpdate(cmd, value, channel, channelId, userId, ove
2260
2251
  if (cmdBase === '/perm') {
2261
2252
  return { error: 'permissionMode 只能通过角色策略编辑器修改', code: 'ROLE_POLICY_MANAGED' };
2262
2253
  }
2263
- const scopeError = validateConfigWriteScope(cmdBase, args);
2254
+ // JSON Menu 入口在 validateMenuRequest 中要求 activity 显式 scope;保留
2255
+ // 底层 execMenuUpdate 的旧默认行为,兼容旧版 slash/内部直调调用方。
2256
+ const scopeError = validateConfigWriteScope(cmdBase, args, { includeActivity: false });
2264
2257
  if (scopeError)
2265
2258
  return { error: scopeError.message, code: scopeError.code };
2266
2259
  args = withoutLegacySessionConfigArgs(args);
@@ -3517,26 +3510,43 @@ const SYSTEM_CONTROL_NAME_MAP = {
3517
3510
  config: '/config', capability: '/capability', role: '/role',
3518
3511
  group: '/group', connect: '/connect',
3519
3512
  };
3513
+ function menuCommandToken(cmd) {
3514
+ if (typeof cmd !== 'string')
3515
+ return undefined;
3516
+ return cmd.trim().split(/\s+/, 1)[0] || undefined;
3517
+ }
3520
3518
  function isProcessLevelMenu(name, cmd) {
3519
+ const cmdToken = menuCommandToken(cmd);
3521
3520
  return name === 'system' || name === 'agent' || name === 'gateway' || name === 'config'
3522
- || cmd === '/system' || cmd === '/agent' || cmd === '/gateway' || cmd === '/config';
3521
+ || cmdToken === '/system' || cmdToken === '/agent' || cmdToken === '/gateway' || cmdToken === '/config';
3523
3522
  }
3524
3523
  async function execMenuForSystemControl(payload, context) {
3525
3524
  const id = payload?.id ?? '';
3526
3525
  const name = payload?.name;
3527
- if (name === 'cli' || payload?.cmd === '/cli') {
3526
+ const compatCmd = menuCommandToken(payload?.cmd);
3527
+ // `menu.query(/file)` returns file metadata and is authorized as
3528
+ // `file.fetch`, while `menu.action(file/fetch)` sends the file. Neither
3529
+ // operation is available through Host/control: file operations must be
3530
+ // addressed to the current Agent AID so the Agent project boundary applies.
3531
+ const isFileFetch = (payload?.type === 'menu.query'
3532
+ || (payload?.type === 'menu.action' && payload?.action === 'fetch'))
3533
+ && (name === 'file' || compatCmd === '/file');
3534
+ if (isFileFetch) {
3535
+ return ecwebErr(id, name, 'NOT_SUPPORTED', 'Host/control 不支持 file.fetch,请通过目标 Agent AID 请求');
3536
+ }
3537
+ if (name === 'cli' || compatCmd === '/cli') {
3528
3538
  const message = context.source === 'ecweb'
3529
3539
  ? 'cli 不在 ECWeb 控制范围'
3530
3540
  : 'cli 不在控制 channel 范围';
3531
3541
  return { type: 'menu.response', id, name, error: { code: 'NOT_SUPPORTED', message } };
3532
3542
  }
3533
3543
  const isProcessLevel = isProcessLevelMenu(name, payload?.cmd);
3534
- const isRoleRequest = name === 'role' || payload?.cmd === '/role';
3535
- const isConnectRequest = name === 'connect' || payload?.cmd === '/connect';
3544
+ const isRoleRequest = name === 'role' || compatCmd === '/role';
3545
+ const isConnectRequest = name === 'connect' || compatCmd === '/connect';
3536
3546
  const requiresAgentTarget = isRoleRequest || isConnectRequest || [
3537
3547
  'observable', 'baseagent', 'model', 'effort', 'trigger', 'capability', 'group',
3538
3548
  ].includes(name)
3539
- || ['/observable', '/baseagent', '/model', '/effort', '/trigger', '/capability', '/group'].includes(payload?.cmd);
3549
+ || ['/observable', '/baseagent', '/model', '/effort', '/trigger', '/capability', '/group'].includes(compatCmd ?? '');
3540
3550
  const target = isProcessLevel
3541
3551
  ? { channel: SYSTEM_CONTROL_CHANNEL }
3542
3552
  : resolveExternalMenuAgentChannel(this, payload, SYSTEM_CONTROL_CHANNEL, requiresAgentTarget);
@@ -3635,8 +3645,9 @@ export async function execMenuForControl(payload, peerId) {
3635
3645
  if (validationError)
3636
3646
  return menuFailure({ id, ...(name ? { name } : {}) }, validationError);
3637
3647
  const owners = loadDaemonConfig().owners ?? [];
3638
- const isRoleRequest = name === 'role' || payload?.cmd === '/role';
3639
- const isConnectRequest = name === 'connect' || payload?.cmd === '/connect';
3648
+ const compatCmd = menuCommandToken(payload?.cmd);
3649
+ const isRoleRequest = name === 'role' || compatCmd === '/role';
3650
+ const isConnectRequest = name === 'connect' || compatCmd === '/connect';
3640
3651
  if (!isRoleRequest && !isConnectRequest && !isProcessLevelOwner(peerId, owners)) {
3641
3652
  return menuFailure({ id, ...(name ? { name } : {}) }, { code: 'ROLE_ACCESS_DENIED', message: '控制 channel 操作需要 owner 权限' });
3642
3653
  }
@@ -15,6 +15,7 @@ const EXPLICIT_SCOPE_UPDATE_NAMES = new Set([
15
15
  'mentionmode',
16
16
  'group',
17
17
  'permission',
18
+ 'activity',
18
19
  ]);
19
20
  const EXPLICIT_SCOPE_UPDATE_COMMANDS = new Set([
20
21
  '/model',
@@ -24,18 +25,20 @@ const EXPLICIT_SCOPE_UPDATE_COMMANDS = new Set([
24
25
  '/mentionmode',
25
26
  '/group',
26
27
  '/perm',
28
+ '/activity',
27
29
  ]);
28
30
  function firstCommandToken(value) {
29
31
  if (typeof value !== 'string')
30
32
  return undefined;
31
33
  return value.trim().split(/\s+/, 1)[0] || undefined;
32
34
  }
33
- function requiresExplicitConfigWriteScope(nameOrCmd) {
35
+ function requiresExplicitConfigWriteScope(nameOrCmd, includeActivity = true) {
34
36
  const token = firstCommandToken(nameOrCmd);
35
- return !!token && (EXPLICIT_SCOPE_UPDATE_NAMES.has(token) || EXPLICIT_SCOPE_UPDATE_COMMANDS.has(token));
37
+ return !!token && (EXPLICIT_SCOPE_UPDATE_NAMES.has(token) || EXPLICIT_SCOPE_UPDATE_COMMANDS.has(token))
38
+ && (includeActivity || (token !== 'activity' && token !== '/activity'));
36
39
  }
37
- export function validateConfigWriteScope(nameOrCmd, args) {
38
- if (!requiresExplicitConfigWriteScope(nameOrCmd))
40
+ export function validateConfigWriteScope(nameOrCmd, args, options) {
41
+ if (!requiresExplicitConfigWriteScope(nameOrCmd, options?.includeActivity ?? true))
39
42
  return null;
40
43
  const rawScope = args?.scope;
41
44
  if (rawScope === undefined || rawScope === null || (typeof rawScope === 'string' && rawScope.trim().length === 0)) {
@@ -1146,7 +1146,7 @@ export async function handleSlashCommand(content, channel, channelId, sendMessag
1146
1146
  }
1147
1147
  catch { }
1148
1148
  const configuredModel = modelConfig.model;
1149
- const configuredEffort = modelConfig[fallbackBaseagent === 'codex' ? 'reasoning' : 'effort'];
1149
+ const configuredEffort = modelConfig.effort;
1150
1150
  const rawModels = hasModelSwitcher(modelAgent) ? await modelAgent.listModels() : [];
1151
1151
  const models = hasModelSwitcher(modelAgent)
1152
1152
  ? filterModelsForRole(identity.role, fallbackBaseagent, rawModels, modelAgent.resolveModelId?.bind(modelAgent))
@@ -1468,7 +1468,7 @@ export async function handleSlashCommand(content, channel, channelId, sendMessag
1468
1468
  }
1469
1469
  catch { }
1470
1470
  const configuredModel = modelConfig.model;
1471
- const configuredEffort = modelConfig[fallbackBaseagent === 'codex' ? 'reasoning' : 'effort'];
1471
+ const configuredEffort = modelConfig.effort;
1472
1472
  const currentModel = hasModelSwitcher(effortAgent) ? (configuredModel || effortAgent.getModel()) : effortAgent.name;
1473
1473
  const efforts = getAvailableEfforts(effortAgent, currentModel);
1474
1474
  const currentEffort = configuredEffort ?? effortAgent.getEffort?.() ?? 'auto';
@@ -1530,7 +1530,7 @@ export async function handleSlashCommand(content, channel, channelId, sendMessag
1530
1530
  }
1531
1531
  catch { }
1532
1532
  const configuredModel = modelConfig.model;
1533
- const configuredEffort = modelConfig[fallbackBaseagent === 'codex' ? 'reasoning' : 'effort'];
1533
+ const configuredEffort = modelConfig.effort;
1534
1534
  const currentModel = hasModelSwitcher(effortAgent) ? (configuredModel || effortAgent.getModel()) : effortAgent.name;
1535
1535
  const efforts = getAvailableEfforts(effortAgent, currentModel);
1536
1536
  const currentEffort = configuredEffort ?? effortAgent.getEffort?.() ?? 'auto';
@@ -2210,7 +2210,7 @@ export async function handleSlashCommand(content, channel, channelId, sendMessag
2210
2210
  }
2211
2211
  catch { }
2212
2212
  const backendModel = modelConfig.model || newRunner.getModel?.() || newAgent?.model;
2213
- const backendEffort = modelConfig[newBaseagent === 'codex' ? 'reasoning' : 'effort']
2213
+ const backendEffort = modelConfig.effort
2214
2214
  ?? newRunner.getEffort?.()
2215
2215
  ?? newAgent?.effort;
2216
2216
  const backendBits = [newBaseagent, backendModel, backendEffort].filter(Boolean).join(' · ');
@@ -3,8 +3,20 @@ function apiEndpoint(baseUrl, resource) {
3
3
  const configured = baseUrl?.trim();
4
4
  if (!configured)
5
5
  throw new Error('Direct text inference baseUrl is not configured');
6
- const base = configured.replace(/\/+$/, '');
7
- return base.endsWith('/v1') ? `${base}/${resource}` : `${base}/v1/${resource}`;
6
+ const parsed = new URL(configured);
7
+ const pathname = parsed.pathname.replace(/\/+$/, '');
8
+ parsed.pathname = pathname.endsWith('/v1')
9
+ ? `${pathname}/${resource}`
10
+ : `${pathname}/v1/${resource}`;
11
+ return parsed.toString();
12
+ }
13
+ function withQueryParams(url, queryParams) {
14
+ if (!queryParams || Object.keys(queryParams).length === 0)
15
+ return url;
16
+ const parsed = new URL(url);
17
+ for (const [name, value] of Object.entries(queryParams))
18
+ parsed.searchParams.set(name, value);
19
+ return parsed.toString();
8
20
  }
9
21
  async function parseError(response) {
10
22
  let raw = '';
@@ -28,10 +40,10 @@ export class AnthropicTextInferenceProvider {
28
40
  this.config = config;
29
41
  }
30
42
  async completeText(request) {
31
- const response = await fetch(apiEndpoint(this.config.baseUrl, 'messages'), {
43
+ const response = await fetch(withQueryParams(apiEndpoint(this.config.baseUrl, 'messages'), this.config.queryParams), {
32
44
  method: 'POST',
33
45
  signal: request.signal,
34
- headers: this.headers(),
46
+ headers: { ...this.headers(), ...this.config.headers },
35
47
  body: JSON.stringify({
36
48
  model: request.model,
37
49
  max_tokens: 256,
@@ -69,12 +81,13 @@ export class OpenAITextInferenceProvider {
69
81
  this.config = config;
70
82
  }
71
83
  async completeText(request) {
72
- const response = await fetch(apiEndpoint(this.config.baseUrl, 'responses'), {
84
+ const response = await fetch(withQueryParams(apiEndpoint(this.config.baseUrl, 'responses'), this.config.queryParams), {
73
85
  method: 'POST',
74
86
  signal: request.signal,
75
87
  headers: {
76
88
  'Content-Type': 'application/json',
77
89
  Authorization: `Bearer ${this.config.apiKey}`,
90
+ ...this.config.headers,
78
91
  },
79
92
  body: JSON.stringify({
80
93
  model: request.model,
@@ -627,6 +627,7 @@ export class MessageBridge {
627
627
  const menuControl = resolvedChannelType === 'aun'
628
628
  ? parseMenuControl(content)
629
629
  : { isMenu: false };
630
+ let menuResponseWarning;
630
631
  if (menuControl.isMenu) {
631
632
  this.logMenuInbound(channelName, msg, menuControl);
632
633
  if (!hasValidMenuId(menuControl)) {
@@ -649,7 +650,7 @@ export class MessageBridge {
649
650
  await this.sendMenuResponse(adapter, channelName, msg, menuFailure(header, {
650
651
  code: 'MENU_TOKEN_ENCRYPTION_REQUIRED',
651
652
  message: 'Menu 请求必须使用 E2EE',
652
- }), true);
653
+ }));
653
654
  return;
654
655
  }
655
656
  if (!isTokenRequest) {
@@ -666,6 +667,13 @@ export class MessageBridge {
666
667
  }));
667
668
  return;
668
669
  }
670
+ if (!menuTokenRequired && tokenSupplied && !tokenValid) {
671
+ this.logMenuDiagnostic('token-invalid-optional', channelName, msg, menuControl, 'MENU_TOKEN_INVALID');
672
+ menuResponseWarning = {
673
+ code: 'MENU_TOKEN_INVALID',
674
+ message: 'Menu token 无效,请重新申请',
675
+ };
676
+ }
669
677
  }
670
678
  const validationError = validateMenuRequest(menuControl.request);
671
679
  if (validationError) {
@@ -712,7 +720,7 @@ export class MessageBridge {
712
720
  });
713
721
  const identity = authSubject.identity;
714
722
  if (menuControl.isMenu) {
715
- await this.handleMenuControl(menuControl, channelName, resolvedChannelType, msg, adapter, authSubject);
723
+ await this.handleMenuControl(menuControl, channelName, resolvedChannelType, msg, adapter, authSubject, menuResponseWarning);
716
724
  return;
717
725
  }
718
726
  // 普通消息保留原始日志;控制 payload 使用脱敏元数据日志。
@@ -1074,7 +1082,7 @@ export class MessageBridge {
1074
1082
  throw { code: 'NOT_SUPPORTED', message: `Unsupported menu name: ${name}` };
1075
1083
  return mapped;
1076
1084
  }
1077
- async handleMenuControl(parsed, channel, channelType, msg, adapter, authSubject) {
1085
+ async handleMenuControl(parsed, channel, channelType, msg, adapter, authSubject, responseWarning) {
1078
1086
  if (!hasValidMenuId(parsed))
1079
1087
  return;
1080
1088
  const header = { id: parsed.id, ...(parsed.name?.trim() ? { name: parsed.name } : {}) };
@@ -1122,7 +1130,10 @@ export class MessageBridge {
1122
1130
  }
1123
1131
  if (deduped.replayed)
1124
1132
  this.logMenuDiagnostic('request-replay', channel, msg, parsed);
1125
- await this.sendMenuResponse(adapter, channel, msg, deduped.value, isTokenRequest);
1133
+ const response = responseWarning && 'data' in deduped.value
1134
+ ? { ...deduped.value, warning: responseWarning }
1135
+ : deduped.value;
1136
+ await this.sendMenuResponse(adapter, channel, msg, response);
1126
1137
  const deferredEffect = afterResponse;
1127
1138
  if (!deduped.replayed && deferredEffect) {
1128
1139
  try {
@@ -1191,7 +1202,7 @@ export class MessageBridge {
1191
1202
  return menuFailure(header, normalizeMenuError(error));
1192
1203
  }
1193
1204
  }
1194
- async sendMenuResponse(adapter, channel, msg, response, forceEncryption = false) {
1205
+ async sendMenuResponse(adapter, channel, msg, response) {
1195
1206
  if (!adapter?.send) {
1196
1207
  this.logMenuDiagnostic('transport-unavailable', channel, msg, {
1197
1208
  isMenu: true,
@@ -1208,7 +1219,7 @@ export class MessageBridge {
1208
1219
  ...(msg.replyContext ?? {}),
1209
1220
  metadata: {
1210
1221
  ...(msg.replyContext?.metadata ?? {}),
1211
- ...evolMenuResponseTransportMetadata(forceEncryption ? true : msg.encrypted),
1222
+ ...evolMenuResponseTransportMetadata(),
1212
1223
  },
1213
1224
  };
1214
1225
  const envelope = buildEnvelope({
@@ -890,6 +890,16 @@ export class ResponseEngine {
890
890
  return;
891
891
  renderer.addNotice(text, severity, subtype, true);
892
892
  }
893
+ async cancelStaleRunnerStart(agent, sessionId, streamKey, turnLease, taskId) {
894
+ if (this.turnCoordinator.isCurrent(turnLease))
895
+ return false;
896
+ logger.info(`[ResponseEngine] Stale turn detected after agent.runQuery start; cancelling unconsumed stream: session=${sessionId} task=${taskId}`);
897
+ await agent.interrupt(sessionId).catch(interruptError => {
898
+ logger.warn(`[ResponseEngine] Failed to cancel stale runner start: session=${sessionId} task=${taskId} error=${interruptError instanceof Error ? interruptError.message : String(interruptError)}`);
899
+ });
900
+ agent.cleanupStream(streamKey);
901
+ return true;
902
+ }
893
903
  async retryAfterContextRecovery(prompt, opts) {
894
904
  const { streamKey, renderer, agent, session, absoluteProjectPath, effectiveSystemPrompt, modelOverride, runtimeEnv, resetTimer, shouldSuppress, proactive, turnLease, } = opts;
895
905
  if (!session.agentSessionId || !canCompactAgent(agent)) {
@@ -911,6 +921,9 @@ export class ResponseEngine {
911
921
  await this.emitOperationalNotice(renderer, '✅ 压缩完成,继续处理...', 'info', 'compact-retry');
912
922
  await renderer.flush();
913
923
  const retryStream = await agent.runQuery(session.id, prompt, absoluteProjectPath, session.agentSessionId, undefined, effectiveSystemPrompt, this.sessionManager, modelOverride, runtimeEnv);
924
+ if (await this.cancelStaleRunnerStart(agent, session.id, streamKey, turnLease, turnLease.taskId)) {
925
+ throw new Error('RUNNER_START_SUPERSEDED');
926
+ }
914
927
  agent.registerStream(streamKey, retryStream);
915
928
  return await this.processEventStream(retryStream, session, agent, renderer, resetTimer, shouldSuppress, proactive, undefined, // 重试分支不调插件钩子
916
929
  undefined, turnLease);
@@ -2331,7 +2344,7 @@ export class ResponseEngine {
2331
2344
  try {
2332
2345
  const selector = { self: selfAid || undefined, peerKey };
2333
2346
  const modelResolution = resolveEffectiveFieldWithSource(`baseagents.${normalizedBaseagent.canonical}.model`, selector, { cache: true });
2334
- const effortResolution = resolveEffectiveFieldWithSource(`baseagents.${normalizedBaseagent.canonical}.${normalizedBaseagent.canonical === 'codex' ? 'reasoning' : 'effort'}`, selector, { cache: true });
2347
+ const effortResolution = resolveEffectiveFieldWithSource(`baseagents.${normalizedBaseagent.canonical}.effort`, selector, { cache: true });
2335
2348
  const modelDecision = constrainResolvedModelForRole({
2336
2349
  role: peerRole,
2337
2350
  baseagent: normalizedBaseagent.canonical,
@@ -2342,7 +2355,7 @@ export class ResponseEngine {
2342
2355
  selfAid: selfAid || undefined,
2343
2356
  });
2344
2357
  const model = modelDecision.model;
2345
- const effortField = `baseagents.${normalizedBaseagent.canonical}.${normalizedBaseagent.canonical === 'codex' ? 'reasoning' : 'effort'}`;
2358
+ const effortField = `baseagents.${normalizedBaseagent.canonical}.effort`;
2346
2359
  const effortDecision = resolveRuntimeStringField({
2347
2360
  selfAid: selfAid || undefined,
2348
2361
  role: peerRole,
@@ -2389,7 +2402,7 @@ export class ResponseEngine {
2389
2402
  effectiveModelSource = triggerModelDecision.constrained ? 'role' : 'trigger';
2390
2403
  }
2391
2404
  if (triggerEffortOverride) {
2392
- const effortField = `baseagents.${normalizedBaseagent.canonical}.${normalizedBaseagent.canonical === 'codex' ? 'reasoning' : 'effort'}`;
2405
+ const effortField = `baseagents.${normalizedBaseagent.canonical}.effort`;
2393
2406
  const triggerEffortDecision = resolveRuntimeStringField({
2394
2407
  selfAid: selfAid || undefined,
2395
2408
  role: peerRole,
@@ -2766,6 +2779,12 @@ export class ResponseEngine {
2766
2779
  try {
2767
2780
  logger.info(`[ResponseEngine] agent.runQuery start: agent=${agent.name} session=${session.id} task=${taskId} attempt=${runAttempt} apiRetries=${retryScheduler.apiRetryAttemptsMade} agentSessionId=${session.agentSessionId ?? 'none'}`);
2768
2781
  const stream = await agent.runQuery(session.id, effectivePrompt, absoluteProjectPath, session.agentSessionId, renderResult?.images.length ? renderResult.images : message.images, effectiveSystemPrompt, this.sessionManager, modelOverride, runtimeEnv);
2782
+ // The turn may be superseded while the runner is still creating its
2783
+ // backend query. Replay the interrupt after runQuery resolves, when
2784
+ // every runner is required to have an addressable cancellation handle.
2785
+ if (await this.cancelStaleRunnerStart(agent, session.id, streamKey, turnLease, taskId)) {
2786
+ throw new Error('RUNNER_START_SUPERSEDED');
2787
+ }
2769
2788
  agent.registerStream(streamKey, stream);
2770
2789
  streamRegistered = true;
2771
2790
  if (shouldTimeoutRetryAttempt) {
@@ -82,9 +82,9 @@ export function activeBaseagent(self) {
82
82
  catch { /* fall through */ }
83
83
  return 'claude';
84
84
  }
85
- /** codex 的推理强度字段名是 reasoning,其余是 effort */
86
- function effortField(ba) {
87
- return ba === 'codex' ? 'reasoning' : 'effort';
85
+ /** 所有 baseagent 统一使用 effort 表达推理强度。 */
86
+ function effortField(_ba) {
87
+ return 'effort';
88
88
  }
89
89
  // ── 写:单作用域 ──────────────────────────────────────────────────────
90
90
  /** 写入指定作用域。patch 中 undefined 不动;显式 null 删除字段。 */
@@ -169,6 +169,17 @@ function isOwnerOrAdminRole(role) {
169
169
  function hasNodeProcessTarget(command) {
170
170
  return /\bnode(?:js|\.exe)?\b/i.test(command);
171
171
  }
172
+ // 包发布命令:npm/pnpm/yarn/bun publish。
173
+ // 排除 --dry-run(不产生实际发布),保留 --provenance 等真实发布参数。
174
+ function isPackagePublishCommand(parsed) {
175
+ const runners = ['npm', 'pnpm', 'yarn', 'bun', 'npx'];
176
+ if (!runners.includes(parsed.executable))
177
+ return false;
178
+ const words = parsed.args.filter(arg => !arg.startsWith('-')).map(arg => arg.toLowerCase());
179
+ if (words[0] !== 'publish')
180
+ return false;
181
+ return !parsed.args.some(arg => arg === '--dry-run' || arg.startsWith('--dry-run='));
182
+ }
172
183
  function classifyHostProcessArgv(argv) {
173
184
  const executable = commandName(argv[0]);
174
185
  if (['ps', 'pgrep', 'pidof'].includes(executable)) {
@@ -279,6 +290,13 @@ function classifyStructuredCommand(command, depth = 0) {
279
290
  matches.push({ kind: 'git-push', reason: 'Git push 会修改远端仓库', approvalScope: 'request' });
280
291
  }
281
292
  }
293
+ if (isPackagePublishCommand(parsed)) {
294
+ matches.push({
295
+ kind: 'package-publish',
296
+ reason: '发布软件包到公共 registry,发布后无法替换同版本',
297
+ approvalScope: 'all',
298
+ });
299
+ }
282
300
  if (['kill', 'pkill', 'killall'].includes(parsed.executable) && hasNodeProcessTarget(parsed.raw)) {
283
301
  matches.push({ kind: 'process-kill', reason: '终止 Node.js 运行时进程', approvalScope: 'request' });
284
302
  }
package/dist/index.js CHANGED
@@ -69,7 +69,7 @@ import { EvolAgentRegistry } from './core/evolagent-registry.js';
69
69
  import { buildReloadHooks } from './core/channel-loader.js';
70
70
  import { IpcServer } from './ipc.js';
71
71
  import { logger, setLogLevel } from './utils/logger.js';
72
- import { fetchEcwebPairCode } from './utils/ecweb-utils.js';
72
+ import { ecwebVersionRequirementError, fetchEcwebPairCode } from './utils/ecweb-utils.js';
73
73
  import { writeMain, removeAll, isMainWinner, scanInstances } from './utils/instance-registry.js';
74
74
  import { detectDuplicates } from './core/evolagent-registry.js';
75
75
  import { loadKitManifest, cleanEckDebug, invalidateKitCache } from './eck/kit-renderer.js';
@@ -369,6 +369,7 @@ async function runBindBootstrapDaemon(daemonCfg) {
369
369
  channelName: 'control',
370
370
  pureIdentity: true,
371
371
  gatewayUrl: daemonCfg.aun?.gatewayUrl,
372
+ defaultEncrypt: daemonCfg.aun?.defaultEncrypt,
372
373
  aunTrace: daemonCfg.debug?.aunTrace,
373
374
  aunSdkLog: daemonCfg.debug?.aunSdkLog,
374
375
  });
@@ -951,7 +952,7 @@ async function main() {
951
952
  messageCache.cleanupExpired();
952
953
  }, 60 * 60 * 1000);
953
954
  // 渠道插件系统
954
- const channelLoader = new ChannelLoader(daemonCfg.debug);
955
+ const channelLoader = new ChannelLoader(daemonCfg.debug, daemonCfg.aun?.defaultEncrypt);
955
956
  channelLoader.register(new FeishuChannelPlugin());
956
957
  channelLoader.register(new WechatChannelPlugin());
957
958
  channelLoader.register(new AUNChannelPlugin());
@@ -1226,7 +1227,7 @@ async function main() {
1226
1227
  const effortDecision = validateRuntimeStringFieldOverride({
1227
1228
  selfAid: definition.agentAid,
1228
1229
  role: actor.role,
1229
- field: `baseagents.${baseagent}.${baseagent === 'codex' ? 'reasoning' : 'effort'}`,
1230
+ field: `baseagents.${baseagent}.effort`,
1230
1231
  value: definition.execution.effort,
1231
1232
  });
1232
1233
  if (!effortDecision.ok)
@@ -1454,7 +1455,7 @@ async function main() {
1454
1455
  originSessionId: originSession.id,
1455
1456
  originMessageId: input.runId,
1456
1457
  payload,
1457
- encrypt: false,
1458
+ encrypt: typeof channel.getDefaultEncrypt === 'function' ? channel.getDefaultEncrypt() : true,
1458
1459
  thread: target.session === 'thread' ? target.threadId : undefined,
1459
1460
  targetChatType,
1460
1461
  explicitReturnPolicy: 'none',
@@ -2016,6 +2017,7 @@ async function main() {
2016
2017
  channelName: 'control',
2017
2018
  pureIdentity: true,
2018
2019
  gatewayUrl: daemonCfg.aun?.gatewayUrl,
2020
+ defaultEncrypt: daemonCfg.aun?.defaultEncrypt,
2019
2021
  aunTrace: daemonCfg.debug?.aunTrace,
2020
2022
  aunSdkLog: daemonCfg.debug?.aunSdkLog,
2021
2023
  });
@@ -2034,26 +2036,38 @@ async function main() {
2034
2036
  const ecwebPath = path.join(resolvePaths().root, 'ecweb');
2035
2037
  const ecwebEntry = path.join(ecwebPath, 'dist', 'index.js');
2036
2038
  if (fs.existsSync(ecwebEntry)) {
2039
+ let ecwebVersion;
2037
2040
  try {
2038
- const ecwebProc = spawn('node', [ecwebEntry], {
2039
- cwd: ecwebPath,
2040
- detached: true,
2041
- stdio: 'ignore',
2042
- env: { ...process.env, EVOLCORE_HOME: resolvePaths().root }
2043
- });
2044
- ecwebProc.unref();
2045
- logger.info(`✓ ECWeb 已启动 (PID: ${ecwebProc.pid})`);
2046
- onShutdown(() => {
2047
- try {
2048
- if (ecwebProc.pid)
2049
- platform.killProcess(ecwebProc.pid, true);
2050
- logger.info(`✓ ECWeb 已停止`);
2051
- }
2052
- catch { }
2053
- });
2041
+ const pkg = JSON.parse(fs.readFileSync(path.join(ecwebPath, 'package.json'), 'utf-8'));
2042
+ ecwebVersion = pkg.version;
2054
2043
  }
2055
- catch (e) {
2056
- logger.warn(`ECWeb 启动失败: ${e?.message || e}`);
2044
+ catch { }
2045
+ const versionError = ecwebVersionRequirementError(ecwebVersion);
2046
+ if (versionError) {
2047
+ logger.error(`ECWeb 未启动: ${versionError}`);
2048
+ }
2049
+ else {
2050
+ try {
2051
+ const ecwebProc = spawn('node', [ecwebEntry], {
2052
+ cwd: ecwebPath,
2053
+ detached: true,
2054
+ stdio: 'ignore',
2055
+ env: { ...process.env, EVOLCORE_HOME: resolvePaths().root }
2056
+ });
2057
+ ecwebProc.unref();
2058
+ logger.info(`✓ ECWeb 已启动 (PID: ${ecwebProc.pid})`);
2059
+ onShutdown(() => {
2060
+ try {
2061
+ if (ecwebProc.pid)
2062
+ platform.killProcess(ecwebProc.pid, true);
2063
+ logger.info(`✓ ECWeb 已停止`);
2064
+ }
2065
+ catch { }
2066
+ });
2067
+ }
2068
+ catch (e) {
2069
+ logger.warn(`ECWeb 启动失败: ${e?.message || e}`);
2070
+ }
2057
2071
  }
2058
2072
  }
2059
2073
  else {
@@ -2090,7 +2104,7 @@ async function main() {
2090
2104
  const isRoleMenu = menuControl.isMenu
2091
2105
  && (menuControl.name === 'role' || menuControl.request.cmd === '/role');
2092
2106
  const menuResponseContext = () => ({
2093
- metadata: evolMenuResponseTransportMetadata(opts.encrypted),
2107
+ metadata: evolMenuResponseTransportMetadata(),
2094
2108
  });
2095
2109
  if (menuControl.isMenu && !hasValidMenuId(menuControl)) {
2096
2110
  logger.warn(`[ControlMenu] dropped malformed request without id type=${menuControl.type}`);
@@ -2479,6 +2493,9 @@ async function main() {
2479
2493
  if ((params.scope === 'group') !== targetIsGroup) {
2480
2494
  return { ok: false, error: 'AUN target does not match the delegated send scope', code: 'UNSUPPORTED_TARGET' };
2481
2495
  }
2496
+ const encrypt = typeof params.encrypt === 'boolean'
2497
+ ? params.encrypt
2498
+ : (typeof ch.getDefaultEncrypt === 'function' ? ch.getDefaultEncrypt() : true);
2482
2499
  let payload = params.payload;
2483
2500
  if (params.file) {
2484
2501
  if (isHClassPath(params.file.filePath)) {
@@ -2520,7 +2537,7 @@ async function main() {
2520
2537
  originSessionId: params.originSessionId,
2521
2538
  originMessageId: params.originMessageId,
2522
2539
  payload,
2523
- encrypt: params.encrypt === true,
2540
+ encrypt,
2524
2541
  thread: params.thread,
2525
2542
  targetChatType: params.scope === 'group' ? 'group' : 'private',
2526
2543
  explicitReturnPolicy: params.returnPolicy,
@@ -2567,7 +2584,7 @@ async function main() {
2567
2584
  groupId: params.to,
2568
2585
  payload,
2569
2586
  mentions: params.mentions,
2570
- encrypt: params.encrypt,
2587
+ encrypt,
2571
2588
  log: params.log ? { ...params.log, sessionId: targetSessionId ?? params.log.sessionId } : undefined,
2572
2589
  });
2573
2590
  }
@@ -2577,7 +2594,7 @@ async function main() {
2577
2594
  return await ch.sendDaemonMsg({
2578
2595
  to: params.to,
2579
2596
  payload,
2580
- encrypt: params.encrypt,
2597
+ encrypt,
2581
2598
  causation: runtimeCausation,
2582
2599
  log: params.log ? { ...params.log, sessionId: targetSessionId ?? params.log.sessionId } : undefined,
2583
2600
  });