evolcore 0.0.38 → 0.0.40

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 (125) hide show
  1. package/CHANGELOG.md +64 -0
  2. package/README.md +31 -20
  3. package/bin/ec-safe-output.js +14 -2
  4. package/dist/agents/baseagent-availability.js +1 -0
  5. package/dist/agents/claude-runner.js +32 -46
  6. package/dist/agents/codex-runner.js +54 -30
  7. package/dist/agents/ecagent-runner.js +18 -7
  8. package/dist/agents/gemini-runner.js +1 -2
  9. package/dist/agents/runner-types.js +28 -6
  10. package/dist/aun/aid/agentmd.js +21 -0
  11. package/dist/aun/aid/client.js +1 -1
  12. package/dist/aun/aid/control-aid.js +23 -98
  13. package/dist/aun/aid/ownership-operation.js +35 -12
  14. package/dist/aun/group/invite-service.js +6 -0
  15. package/dist/aun/msg/p2p.js +8 -12
  16. package/dist/channels/aun.js +66 -48
  17. package/dist/channels/dingtalk.js +8 -2
  18. package/dist/channels/feishu.js +6 -1
  19. package/dist/channels/wecom.js +7 -3
  20. package/dist/cli/agent-create.js +30 -6
  21. package/dist/cli/agent.js +30 -37
  22. package/dist/cli/aid-bind-command.js +16 -2
  23. package/dist/cli/aun-commands.js +31 -23
  24. package/dist/cli/config.js +15 -18
  25. package/dist/cli/contact.js +9 -12
  26. package/dist/cli/ctl-command.js +3 -3
  27. package/dist/cli/daemon-commands.js +242 -17
  28. package/dist/cli/diag.js +7 -3
  29. package/dist/cli/fs-command.js +5 -4
  30. package/dist/cli/handoff-command.js +11 -11
  31. package/dist/cli/help.js +18 -0
  32. package/dist/cli/index.js +25 -13
  33. package/dist/cli/init-channel.js +2 -1
  34. package/dist/cli/memory-command.js +47 -27
  35. package/dist/cli/model.js +5 -4
  36. package/dist/cli/queue-command.js +3 -3
  37. package/dist/cli/response.js +5 -4
  38. package/dist/cli/restart-monitor.js +35 -13
  39. package/dist/cli/skill-command.js +13 -13
  40. package/dist/cli/task-command.js +3 -3
  41. package/dist/cli/task-context.js +1 -0
  42. package/dist/cli/thread-command.js +5 -3
  43. package/dist/cli/trigger-command.js +3 -3
  44. package/dist/cli/version.js +6 -2
  45. package/dist/config/config-field-policy.js +1 -0
  46. package/dist/contracts/ecweb-service-protocol.js +44 -0
  47. package/dist/core/agent-runtime.js +6 -0
  48. package/dist/core/auth/agent-delegation.js +7 -4
  49. package/dist/core/auth/auth-gateway.js +3 -1
  50. package/dist/core/auth/authorization-audit.js +5 -1
  51. package/dist/core/auth/debug-context.js +5 -1
  52. package/dist/core/auth/operation-catalog.js +17 -1
  53. package/dist/core/bootstrap-service.js +20 -12
  54. package/dist/core/command/cli-intent-parser.js +5 -2
  55. package/dist/core/command/command-handler.js +20 -5
  56. package/dist/core/command/menu-catalog.js +48 -12
  57. package/dist/core/command/menu-handler.js +74 -6
  58. package/dist/core/command/menu-protocol.js +20 -1
  59. package/dist/core/command/slash-gate.js +17 -3
  60. package/dist/core/command/slash-handler.js +90 -7
  61. package/dist/core/event-catalog.js +2 -0
  62. package/dist/core/interaction-router.js +2 -1
  63. package/dist/core/ipc-registration.js +26 -7
  64. package/dist/core/memory/batch-meta.js +2 -0
  65. package/dist/core/memory/consolidation-context.js +42 -51
  66. package/dist/core/memory/consolidation-ops-records.js +39 -7
  67. package/dist/core/memory/consolidation-trigger.js +71 -64
  68. package/dist/core/memory/consolidation.js +226 -20
  69. package/dist/core/memory/memory-writer.js +160 -34
  70. package/dist/core/memory/relation-idle-monitor.js +287 -0
  71. package/dist/core/memory/relation-sweep.js +206 -0
  72. package/dist/core/message/aun-ipc-sender.js +5 -0
  73. package/dist/core/message/message-bridge.js +64 -55
  74. package/dist/core/message/message-log.js +17 -0
  75. package/dist/core/message/message-queue.js +6 -0
  76. package/dist/core/message/outbound.js +70 -0
  77. package/dist/core/message/peer-mode.js +7 -2
  78. package/dist/core/message/response-engine.js +341 -73
  79. package/dist/core/ownership/owner-resolver.js +465 -140
  80. package/dist/core/permission/ec-command-parser.js +19 -3
  81. package/dist/core/permission/protected-paths.js +51 -0
  82. package/dist/core/permission/tool-policy.js +7 -7
  83. package/dist/core/role/runtime-policy.js +64 -0
  84. package/dist/core/session/session-manager.js +25 -5
  85. package/dist/core/system-session/memory.js +3 -18
  86. package/dist/ecweb/basic-settings-service.js +709 -0
  87. package/dist/ecweb/providers-service.js +172 -0
  88. package/dist/index.js +92 -4
  89. package/dist/ipc.js +78 -11
  90. package/dist/stats/db.js +33 -0
  91. package/dist/stats/index.js +2 -2
  92. package/dist/stats/query.js +246 -0
  93. package/dist/stats/writer.js +41 -1
  94. package/dist/trigger/application-service.js +12 -0
  95. package/dist/trigger/authorization.js +2 -2
  96. package/dist/trigger/history.js +36 -0
  97. package/dist/trigger/scheduler.js +3 -0
  98. package/dist/utils/ecweb-supervisor.js +31 -10
  99. package/dist/utils/error-utils.js +8 -4
  100. package/dist/utils/evolcore-version.js +3 -2
  101. package/dist/utils/host-info.js +52 -0
  102. package/dist/utils/welcome.js +18 -24
  103. package/ecagent/dist/harness/agent-harness.js +4 -1
  104. package/kits/docs/evolcore/INDEX.md +1 -1
  105. package/kits/docs/evolcore/agent.md +1 -1
  106. package/kits/docs/evolcore/aid.md +58 -8
  107. package/kits/docs/evolcore/config.md +9 -7
  108. package/kits/docs/evolcore/memory-architecture.md +3 -4
  109. package/kits/docs/evolcore/memory-consolidation.md +28 -7
  110. package/kits/docs/evolcore/memory-tags.md +1 -1
  111. package/kits/docs/evolcore/memory.md +1 -1
  112. package/kits/docs/evolcore/menu-client.md +1 -1
  113. package/kits/docs/evolcore/menu-protocol.md +6 -0
  114. package/kits/docs/evolcore/msg.md +1 -1
  115. package/kits/eck_manifest.json +3 -3
  116. package/kits/rules/04-relation.md +3 -2
  117. package/kits/schemas/agent-config.schema.13.json +5 -1
  118. package/kits/templates/memory-consolidation/generalize.md +32 -4
  119. package/kits/templates/memory-consolidation/notes.md +2 -2
  120. package/kits/templates/memory-consolidation/promote.md +10 -2
  121. package/kits/templates/system-fragments/baseagent.md +5 -0
  122. package/kits/templates/system-fragments/memory.md +12 -3
  123. package/kits/templates/system-fragments/session.md +1 -1
  124. package/kits/{rules/07-memory-and-thread.md → templates/system-fragments/thread.md} +17 -80
  125. package/package.json +3 -3
@@ -4,7 +4,7 @@ import crypto from 'crypto';
4
4
  import { agentMdPath, resolvePaths, resolveRoot } from '../paths.js';
5
5
  import { isExplicitGroupId, normalizeGroupAid, sameGroupAid } from '../aun/group/index.js';
6
6
  import { getArgValue, isHelpFlag, wantsHelp } from './help.js';
7
- import { ipcQuery } from '../ipc.js';
7
+ import { daemonUnavailableMessage, queryDaemon } from './help.js';
8
8
  import { isValidAid as validSendAid } from '../aun/aid/validation.js';
9
9
  import { AGENT_DELEGATION_TOKEN_ENV, readDelegationCredential } from '../core/auth/agent-delegation.js';
10
10
  import { isManagementRole } from '../config/builtin-roles.js';
@@ -137,14 +137,15 @@ async function runManagedAid(args, formatJson) {
137
137
  if (requiresSelfAid && runtime?.selfAid && parsed.command.aid !== runtime.selfAid.replace(/^@/, '')) {
138
138
  failManagedAid(formatJson, 'SCOPE_MISMATCH', `托管任务只能操作当前 self AID:${runtime.selfAid}`);
139
139
  }
140
- const response = await ipcQuery(resolvePaths().socket, {
140
+ const { response, transportError } = await queryDaemon(resolvePaths().socket, {
141
141
  type: 'aid.op',
142
142
  argv: ['aid', ...args],
143
143
  sessionId,
144
144
  delegationToken,
145
145
  }, 30_000);
146
- if (!response)
147
- failManagedAid(formatJson, 'DAEMON_UNAVAILABLE', 'Unable to connect to the evolcore daemon');
146
+ if (!response) {
147
+ failManagedAid(formatJson, 'DAEMON_UNAVAILABLE', daemonUnavailableMessage(transportError, 'Unable to connect to the evolcore daemon'));
148
+ }
148
149
  if (!response.ok || !response.result) {
149
150
  const error = response.error || 'AID operation failed';
150
151
  const aid = args[0] === 'agentmd' ? args[2] : args[1];
@@ -220,14 +221,15 @@ async function runManagedGroup(args, formatJson) {
220
221
  failManagedGroup(formatJson, parsed.code, parsed.reason);
221
222
  const runtime = await readBestTaskRuntimeContext();
222
223
  validateManagedGroupScope(parsed.command.from, parsed.command.groupId, runtime, formatJson);
223
- const response = await ipcQuery(resolvePaths().socket, {
224
+ const { response, transportError } = await queryDaemon(resolvePaths().socket, {
224
225
  type: 'group.op',
225
226
  argv: ['group', ...args],
226
227
  sessionId,
227
228
  delegationToken,
228
229
  }, 60_000);
229
- if (!response)
230
- failManagedGroup(formatJson, 'DAEMON_UNAVAILABLE', 'Unable to connect to the evolcore daemon');
230
+ if (!response) {
231
+ failManagedGroup(formatJson, 'DAEMON_UNAVAILABLE', daemonUnavailableMessage(transportError, 'Unable to connect to the evolcore daemon'));
232
+ }
231
233
  if (!response.ok || !response.result) {
232
234
  failManagedGroup(formatJson, response.code || 'GROUP_OPERATION_FAILED', response.error || 'Group operation failed');
233
235
  }
@@ -277,14 +279,15 @@ async function runManagedMsg(args, formatJson) {
277
279
  failManagedGroup(formatJson, 'SCOPE_MISMATCH', `只能读取当前 AUN 私聊对端:${runtime.channelId}`);
278
280
  }
279
281
  }
280
- const response = await ipcQuery(resolvePaths().socket, {
282
+ const { response, transportError } = await queryDaemon(resolvePaths().socket, {
281
283
  type: 'msg.op',
282
284
  argv: ['msg', ...args],
283
285
  sessionId,
284
286
  delegationToken,
285
287
  }, 30_000);
286
- if (!response)
287
- failManagedGroup(formatJson, 'DAEMON_UNAVAILABLE', 'Unable to connect to the evolcore daemon');
288
+ if (!response) {
289
+ failManagedGroup(formatJson, 'DAEMON_UNAVAILABLE', daemonUnavailableMessage(transportError, 'Unable to connect to the evolcore daemon'));
290
+ }
288
291
  if (!response.ok || !response.result) {
289
292
  failManagedGroup(formatJson, response.code || 'MSG_OPERATION_FAILED', response.error || 'Msg operation failed');
290
293
  }
@@ -469,6 +472,8 @@ Commands:
469
472
  avatar <aid> --file <path> | --url <https-url> 上传头像并更新 agent.md
470
473
  bind <ownerAid> [--subject <subjectAid>] 发起 Ownership 2.1 绑定
471
474
  bind status [--subject <subjectAid>] 核验协议 Owner、任务和同步状态
475
+ bind accept <operationId> [--subject <subjectAid>] 接受绑定邀约
476
+ bind reject <operationId> [--reason <text>] [--subject <subjectAid>] 拒绝绑定邀约
472
477
 
473
478
  Options:
474
479
  --format json 输出 JSON 格式
@@ -1159,7 +1164,7 @@ Options:
1159
1164
  console.error('用法: ec storage upload <aid> <local-file> <remote-path> [--public]');
1160
1165
  process.exit(1);
1161
1166
  }
1162
- const result = await ipcQuery(resolvePaths().socket, {
1167
+ const { response: result, transportError } = await queryDaemon(resolvePaths().socket, {
1163
1168
  type: 'storage.upload',
1164
1169
  argv: ['storage', ...args],
1165
1170
  aid,
@@ -1169,8 +1174,9 @@ Options:
1169
1174
  sessionId: process.env.EVOLCORE_SESSION_ID,
1170
1175
  delegationToken,
1171
1176
  }, null);
1172
- if (!result)
1173
- failManagedGroup(formatJson, 'DAEMON_UNAVAILABLE', 'Unable to connect to the evolcore daemon');
1177
+ if (!result) {
1178
+ failManagedGroup(formatJson, 'DAEMON_UNAVAILABLE', daemonUnavailableMessage(transportError, 'Unable to connect to the evolcore daemon'));
1179
+ }
1174
1180
  if (!result.ok)
1175
1181
  failManagedGroup(formatJson, result.code || 'STORAGE_UPLOAD_FAILED', result.error || 'Storage upload failed');
1176
1182
  if (formatJson)
@@ -1185,11 +1191,12 @@ Options:
1185
1191
  }
1186
1192
  return;
1187
1193
  }
1188
- const authorization = await ipcQuery(resolvePaths().socket, {
1194
+ const { response: authorization, transportError } = await queryDaemon(resolvePaths().socket, {
1189
1195
  type: 'storage.auth', argv: ['storage', ...args], operation, aid, sessionId: process.env.EVOLCORE_SESSION_ID, delegationToken,
1190
1196
  }, 10_000);
1191
- if (!authorization)
1192
- failManagedGroup(formatJson, 'DAEMON_UNAVAILABLE', 'Unable to connect to the evolcore daemon');
1197
+ if (!authorization) {
1198
+ failManagedGroup(formatJson, 'DAEMON_UNAVAILABLE', daemonUnavailableMessage(transportError, 'Unable to connect to the evolcore daemon'));
1199
+ }
1193
1200
  if (!authorization.ok)
1194
1201
  failManagedGroup(formatJson, authorization.code || 'STORAGE_AUTH_FAILED', authorization.error || 'Storage operation denied');
1195
1202
  if (sub === 'ls') {
@@ -1531,7 +1538,7 @@ async function applyCrossThreadSendEffects(input) {
1531
1538
  originThreadName,
1532
1539
  targetThreadName,
1533
1540
  });
1534
- const triggerRes = await ipcQuery(resolvePaths().socket, {
1541
+ const { response: triggerRes, transportError } = await queryDaemon(resolvePaths().socket, {
1535
1542
  type: 'trigger.create',
1536
1543
  definition: triggerDef,
1537
1544
  files: [],
@@ -1543,7 +1550,7 @@ async function applyCrossThreadSendEffects(input) {
1543
1550
  delegationToken: readDelegationCredential(),
1544
1551
  }, 30_000);
1545
1552
  if (!triggerRes?.ok) {
1546
- process.stderr.write(`⚠ Trigger 创建失败: ${triggerRes?.error ?? 'unknown'}(inbox 仍会写入)\n`);
1553
+ process.stderr.write(`⚠ Trigger 创建失败: ${triggerRes?.error ?? daemonUnavailableMessage(transportError, 'unknown')}(inbox 仍会写入)\n`);
1547
1554
  }
1548
1555
  else {
1549
1556
  // 以 daemon 落盘的结果为准(definition 已带 id,正常应与预设值相同;
@@ -1691,13 +1698,13 @@ Options:
1691
1698
  console.error(`request status is limited to the delegated self AID: ${runtime.selfAid}`);
1692
1699
  process.exit(1);
1693
1700
  }
1694
- const result = await ipcQuery(resolvePaths().socket, {
1701
+ const { response: result, transportError } = await queryDaemon(resolvePaths().socket, {
1695
1702
  type: 'aun-msg-request-get', aid, requestId,
1696
1703
  sessionId: process.env.EVOLCORE_SESSION_ID,
1697
1704
  delegationToken: readDelegationCredential(),
1698
1705
  }, 10_000);
1699
1706
  if (!result) {
1700
- console.error('unable to connect to the EvolCore daemon');
1707
+ console.error(daemonUnavailableMessage(transportError, 'unable to connect to the EvolCore daemon'));
1701
1708
  process.exit(1);
1702
1709
  }
1703
1710
  if (formatJson)
@@ -3388,12 +3395,13 @@ async function runManagedSend(argv, formatJson) {
3388
3395
  if (!delegationToken)
3389
3396
  failManagedGroup(formatJson, 'DELEGATION_REQUIRED', 'Managed send requires delegation');
3390
3397
  const runtime = await readBestTaskRuntimeContext();
3391
- const result = await ipcQuery(resolvePaths().socket, {
3398
+ const { response: result, transportError } = await queryDaemon(resolvePaths().socket, {
3392
3399
  type: 'aun-msg-send', argv, delegationToken,
3393
3400
  originSessionId: process.env.EVOLCORE_SESSION_ID, originMessageId: runtime?.messageId,
3394
3401
  }, null);
3395
- if (!result)
3396
- failManagedGroup(formatJson, 'AUN_DAEMON_UNAVAILABLE', 'Unable to connect to the EvolCore daemon');
3402
+ if (!result) {
3403
+ failManagedGroup(formatJson, 'AUN_DAEMON_UNAVAILABLE', daemonUnavailableMessage(transportError, 'Unable to connect to the EvolCore daemon'));
3404
+ }
3397
3405
  if (!result.ok)
3398
3406
  failManagedGroup(formatJson, result.code ?? 'AUN_SEND_FAILED', `${result.error ?? 'Send failed'}${result.request_id ? ` request_id=${result.request_id}` : ''}`, {
3399
3407
  ...(typeof result.detail_code === 'string' ? { detail_code: result.detail_code } : {}),
@@ -2,7 +2,7 @@ import { isHelpFlag, wantsHelp, getArgValue } from './help.js';
2
2
  import { initConfigManager } from '../config/config-manager.js';
3
3
  import { resolveConfigCommand } from '../config/resolved-config-op.js';
4
4
  import { AUTHENTICATED_CONFIG_ROLE_ENV, executeResolvedConfigCommandAsync, } from '../config/config-operation-service.js';
5
- import { ipcQuery } from '../ipc.js';
5
+ import { daemonUnavailableMessage, queryDaemon } from './help.js';
6
6
  import { resolvePaths } from '../paths.js';
7
7
  import { AGENT_DELEGATION_TOKEN_ENV, readDelegationCredential } from '../core/auth/agent-delegation.js';
8
8
  import { splitConfigBatchGetArgv } from './cli-argv.js';
@@ -135,23 +135,19 @@ function managedSession(formatJson) {
135
135
  return { sessionId, delegationToken };
136
136
  }
137
137
  async function managedConfigQuery(args, sessionId, delegationToken) {
138
- try {
139
- return await ipcQuery(resolvePaths().socket, {
140
- type: 'config.op',
141
- argv: ['config', ...args],
142
- sessionId,
143
- delegationToken,
144
- }, 10_000);
145
- }
146
- catch {
147
- return null;
148
- }
138
+ return queryDaemon(resolvePaths().socket, {
139
+ type: 'config.op',
140
+ argv: ['config', ...args],
141
+ sessionId,
142
+ delegationToken,
143
+ }, 10_000);
149
144
  }
150
145
  async function runManagedConfig(args, formatJson) {
151
146
  const { sessionId, delegationToken } = managedSession(formatJson);
152
- const response = await managedConfigQuery(args, sessionId, delegationToken);
153
- if (!response)
154
- fail(formatJson, 'DAEMON_UNAVAILABLE', 'Unable to connect to the evolcore daemon');
147
+ const { response, transportError } = await managedConfigQuery(args, sessionId, delegationToken);
148
+ if (!response) {
149
+ fail(formatJson, 'DAEMON_UNAVAILABLE', daemonUnavailableMessage(transportError, 'Unable to connect to the evolcore daemon'));
150
+ }
155
151
  if (!response.ok || !response.result) {
156
152
  fail(formatJson, response.code || 'CONFIG_AUTH_FAILED', response.error || 'config operation failed', response.data);
157
153
  }
@@ -197,9 +193,10 @@ async function runDirectBatchGet(fields, rest, formatJson) {
197
193
  }
198
194
  async function runManagedBatchGet(fields, rest, formatJson) {
199
195
  const { sessionId, delegationToken } = managedSession(formatJson);
200
- const response = await managedConfigQuery(['get', ...fields, ...rest], sessionId, delegationToken);
201
- if (!response)
202
- fail(formatJson, 'DAEMON_UNAVAILABLE', 'Unable to connect to the evolcore daemon');
196
+ const { response, transportError } = await managedConfigQuery(['get', ...fields, ...rest], sessionId, delegationToken);
197
+ if (!response) {
198
+ fail(formatJson, 'DAEMON_UNAVAILABLE', daemonUnavailableMessage(transportError, 'Unable to connect to the evolcore daemon'));
199
+ }
203
200
  if (!response.ok || !response.result) {
204
201
  fail(formatJson, response.code || 'CONFIG_AUTH_FAILED', response.error || 'config operation failed', response.data);
205
202
  }
@@ -1,7 +1,7 @@
1
1
  import { resolvePaths } from '../paths.js';
2
2
  import { loadAllAgents } from '../config-store.js';
3
3
  import { executeResolvedContactCommand, resolveContactCommand, } from '../config/contact-operation-service.js';
4
- import { ipcQuery } from '../ipc.js';
4
+ import { daemonUnavailableMessage, queryDaemon } from './help.js';
5
5
  import { AGENT_DELEGATION_TOKEN_ENV, readDelegationCredential } from '../core/auth/agent-delegation.js';
6
6
  import { getArgValue, isHelpFlag, wantsHelp } from './help.js';
7
7
  export async function cmdContact(args) {
@@ -48,18 +48,15 @@ async function runManaged(args, json) {
48
48
  const delegationToken = readDelegationCredential();
49
49
  if (!delegationToken)
50
50
  fail(`${AGENT_DELEGATION_TOKEN_ENV} is not set for the active task`, 'DELEGATION_REQUIRED');
51
- let response = null;
52
- try {
53
- response = await ipcQuery(resolvePaths().socket, {
54
- type: 'contact.op',
55
- argv: ['contact', ...args],
56
- sessionId,
57
- delegationToken,
58
- }, 10_000);
51
+ const { response, transportError } = await queryDaemon(resolvePaths().socket, {
52
+ type: 'contact.op',
53
+ argv: ['contact', ...args],
54
+ sessionId,
55
+ delegationToken,
56
+ }, 10_000);
57
+ if (!response) {
58
+ fail(daemonUnavailableMessage(transportError, 'Unable to connect to the evolcore daemon'), 'DAEMON_UNAVAILABLE');
59
59
  }
60
- catch { }
61
- if (!response)
62
- fail('Unable to connect to the evolcore daemon', 'DAEMON_UNAVAILABLE');
63
60
  if (!response.ok || !response.result) {
64
61
  fail(response.error || 'contact operation failed', response.code || 'CONTACT_AUTH_FAILED', response.data);
65
62
  }
@@ -1,5 +1,5 @@
1
1
  import { resolvePaths } from '../paths.js';
2
- import { ipcQuery } from '../ipc.js';
2
+ import { daemonUnavailableMessage, queryDaemon } from './help.js';
3
3
  import { isHelpFlag, wantsHelp } from './help.js';
4
4
  import { readDelegationCredential } from '../core/auth/agent-delegation.js';
5
5
  // ==================== Ctl ====================
@@ -44,14 +44,14 @@ export async function cmdCtl(args) {
44
44
  process.exit(1);
45
45
  }
46
46
  const socketPath = resolvePaths().socket;
47
- const result = await ipcQuery(socketPath, {
47
+ const { response: result, transportError } = await queryDaemon(socketPath, {
48
48
  type: 'ctl',
49
49
  argv: ['ctl', ...args],
50
50
  sessionId,
51
51
  delegationToken: readDelegationCredential(),
52
52
  }, 10_000);
53
53
  if (!result) {
54
- console.error('错误: 无法连接 evolcore 服务');
54
+ console.error(`错误: ${daemonUnavailableMessage(transportError, '无法连接 evolcore 服务')}`);
55
55
  process.exit(1);
56
56
  }
57
57
  const ctlResult = result;
@@ -31,6 +31,8 @@ import { rotateStdoutLog } from '../utils/log-writer.js';
31
31
  import { inspectDataMigrationRequirement } from '../core/data-migration.js';
32
32
  import { isInitCancelledError, printInitCancelHint } from './init-cancel.js';
33
33
  import { computeBuildId } from '../utils/evolcore-version.js';
34
+ import { readHostInfo } from '../utils/host-info.js';
35
+ import { queryStatusActivity, queryStatusActivityByAgent } from '../stats/query.js';
34
36
  import { listCodexAppServerProcesses, unregisterCodexAppServerProcess } from '../agents/codex/app-server-registry.js';
35
37
  import { DEFAULT_ECWEB_PORT, ecwebService, setEcwebEnabled } from '../config/daemon-services.js';
36
38
  import { checkCodexProjectPath, inspectCodexManagedHook, ensureCodexManagedHook, runCodexHookLaunchDiagnostic, } from '../agents/codex/diagnostics.js';
@@ -301,7 +303,20 @@ async function requireFreshSourceBuild() {
301
303
  async function launchPackageLifecycle(source, log, options = {}) {
302
304
  const correlated = options.requestId ?? process.env[RESTART_REQUEST_ID_ENV];
303
305
  const upgradeJobId = correlated ? readRestartRequest(path.join(daemonControlDir(), 'restart-pending.json'), correlated)?.upgradeJobId : undefined;
304
- if (!options.prepared && !upgradeJobId)
306
+ let configuredAutoUpgrade = true;
307
+ if (!upgradeJobId && options.applyPackageUpgrades === undefined) {
308
+ try {
309
+ configuredAutoUpgrade = loadDaemonConfig().upgrade?.autoOnStart !== false;
310
+ }
311
+ catch {
312
+ configuredAutoUpgrade = false;
313
+ log('Could not read autoOnStart; skipping automatic package upgrade');
314
+ }
315
+ }
316
+ const applyPackageUpgrades = upgradeJobId
317
+ ? true
318
+ : options.applyPackageUpgrades ?? configuredAutoUpgrade;
319
+ if (!options.prepared && !upgradeJobId && applyPackageUpgrades)
305
320
  await ensureFreshPackageVersionInfo(undefined, { source, log });
306
321
  const root = resolvePaths().root;
307
322
  const launch = platform.resolveMonitorLaunch(getPackageRoot(), path.join(root, 'data', 'monitor-stage'));
@@ -313,7 +328,12 @@ async function launchPackageLifecycle(source, log, options = {}) {
313
328
  return { ok: false, reason: 'RESTART_ENTRY_MISSING' };
314
329
  }
315
330
  const requestId = options.requestId ?? process.env[RESTART_REQUEST_ID_ENV];
316
- const args = [...launch.args, 'restart-monitor', ...(options.clear ? ['--clear'] : [])];
331
+ const args = [
332
+ ...launch.args,
333
+ 'restart-monitor',
334
+ ...(options.clear ? ['--clear'] : []),
335
+ applyPackageUpgrades ? '--apply-package-upgrades' : '--skip-package-upgrades',
336
+ ];
317
337
  const env = {
318
338
  ...options.env,
319
339
  [RUNTIME_HOME_ENV]: root,
@@ -758,7 +778,7 @@ export async function cmdStart(opts = {}) {
758
778
  // 这条路由 restart-monitor 拉起 daemon,同样靠覆盖文件把 --as-mode 带过去
759
779
  const overrideFile = writeAsModeOverride(opts.asMode);
760
780
  const launched = await launchPackageLifecycle('start', console.log, {
761
- prepared: true, foreground: opts.foreground, env: buildDaemonEnv(p, opts),
781
+ prepared: true, foreground: opts.foreground, env: buildDaemonEnv(p, opts), applyPackageUpgrades: true,
762
782
  });
763
783
  if (!launched.ok) {
764
784
  if (!launched.pending)
@@ -1291,7 +1311,180 @@ function showConfigChannels(config) {
1291
1311
  console.log(' (no channels configured)');
1292
1312
  }
1293
1313
  }
1294
- export async function cmdStatus() {
1314
+ function showLiveChannels(status) {
1315
+ const channels = status?.channels;
1316
+ if (!channels || typeof channels !== 'object' || Array.isArray(channels))
1317
+ return false;
1318
+ const entries = Object.entries(channels);
1319
+ console.log('🔌 Channels (live status):');
1320
+ if (entries.length === 0) {
1321
+ console.log(' (no channel instances loaded)');
1322
+ return true;
1323
+ }
1324
+ for (const [name, channel] of entries) {
1325
+ const state = channel?.connected === true ? '✓ Connected'
1326
+ : channel?.connected === false ? '✗ Disconnected' : '? Unknown';
1327
+ const type = typeof channel?.channelType === 'string' ? ` (${channel.channelType})` : '';
1328
+ console.log(` ${name}: ${state}${type}`);
1329
+ }
1330
+ return true;
1331
+ }
1332
+ function localDayStartMs(now = new Date()) {
1333
+ return new Date(now.getFullYear(), now.getMonth(), now.getDate()).getTime();
1334
+ }
1335
+ function statusActivityJson(window) {
1336
+ return {
1337
+ received: window.received,
1338
+ sent: window.sent,
1339
+ completed: window.completed,
1340
+ errors: window.errors,
1341
+ interrupts: window.interrupts,
1342
+ avgResponseMs: window.avgResponseMs,
1343
+ modelCalls: window.modelCalls,
1344
+ tokens: window.tokens,
1345
+ cost: window.cost,
1346
+ };
1347
+ }
1348
+ function readLocalPackageVersion() {
1349
+ try {
1350
+ const pkg = JSON.parse(fs.readFileSync(path.join(getPackageRoot(), 'package.json'), 'utf8'));
1351
+ return typeof pkg.version === 'string' ? pkg.version : null;
1352
+ }
1353
+ catch {
1354
+ return null;
1355
+ }
1356
+ }
1357
+ async function cmdStatusJson() {
1358
+ const p = resolvePaths();
1359
+ const started = Date.now();
1360
+ const instanceStatus = scanInstances();
1361
+ const aliveMains = instanceStatus.mains.filter(instance => instance.alive);
1362
+ let ipcError;
1363
+ const pingPromise = probeDaemon(p.socket, DAEMON_PROBE_TIMEOUT_MS, true).catch(error => {
1364
+ ipcError = error;
1365
+ return null;
1366
+ });
1367
+ const [ping, statusResponse, aidsResponse, agentsResponse, agentStatsResponse] = await Promise.all([
1368
+ pingPromise,
1369
+ ipcQuery(p.socket, { type: 'status' }).catch(() => null),
1370
+ ipcQuery(p.socket, { type: 'aun-aids' }).catch(() => null),
1371
+ ipcQuery(p.socket, { type: 'evolagent.list' }).catch(() => null),
1372
+ ipcQuery(p.socket, { type: 'agent-stats' }).catch(() => null),
1373
+ ]);
1374
+ const now = Date.now();
1375
+ const runningPid = ping?.pid ?? aliveMains[0]?.record.pid ?? null;
1376
+ const mainRecord = runningPid ? aliveMains.find(instance => instance.record.pid === runningPid)?.record : undefined;
1377
+ const statusAvailable = !!statusResponse;
1378
+ const overallStatus = statusAvailable
1379
+ ? 'running'
1380
+ : platform.isWindows && ipcError instanceof IpcTransportError
1381
+ && ['ENOENT', 'ECONNREFUSED'].includes(ipcError.code)
1382
+ ? 'stopped'
1383
+ : 'unknown';
1384
+ const todayStart = localDayStartMs(new Date(now));
1385
+ const lastHourActivity = queryStatusActivity(p.root, now - 3_600_000, now);
1386
+ const todayActivity = statusResponse?.activity?.today ?? queryStatusActivity(p.root, todayStart, now);
1387
+ const daemonLastHourActivity = statusResponse?.activity?.lastHour;
1388
+ const effectiveLastHourActivity = daemonLastHourActivity ?? lastHourActivity;
1389
+ const agents = agentsResponse?.agents ?? [];
1390
+ const agentStats = new Map((agentStatsResponse?.stats ?? []).map((item) => [item.aid, item]));
1391
+ const statusAgents = Array.isArray(statusResponse?.agents) ? statusResponse.agents : null;
1392
+ const agentUsage = statusAgents
1393
+ ? new Map()
1394
+ : queryStatusActivityByAgent(p.root, todayStart, now, agents.map((agent) => String(agent.aid ?? agent.name ?? '')));
1395
+ const diagnostics = [];
1396
+ if (!statusAvailable) {
1397
+ diagnostics.push({ code: 'DAEMON_IPC_UNAVAILABLE', message: ipcError instanceof Error ? ipcError.message : 'daemon status unavailable' });
1398
+ }
1399
+ const agentItems = statusAgents ?? agents.map((agent) => {
1400
+ const aid = String(agent.aid ?? agent.name ?? '');
1401
+ const runtime = agentStats.get(aid);
1402
+ const usage = agentUsage.get(aid) ?? queryStatusActivity(p.root, todayStart, now, aid);
1403
+ return {
1404
+ aid,
1405
+ name: agent.name ?? aid,
1406
+ status: agent.status ?? 'unknown',
1407
+ baseagent: agent.baseagent ?? null,
1408
+ model: agent.model ?? null,
1409
+ effort: agent.effort ?? null,
1410
+ queue: runtime ? { pending: runtime.queued ?? 0, processing: runtime.processing ?? 0 } : null,
1411
+ usage: {
1412
+ calls: usage.modelCalls,
1413
+ tokens: usage.tokens,
1414
+ cost: usage.cost,
1415
+ },
1416
+ };
1417
+ });
1418
+ const channels = Object.entries(statusResponse?.channels ?? {})
1419
+ .filter(([, channel]) => channel?.channelType !== 'aun')
1420
+ .map(([name, channel]) => ({
1421
+ name,
1422
+ type: channel?.channelType ?? name,
1423
+ status: channel?.status ?? (channel?.connected ? 'connected' : 'disconnected'),
1424
+ connected: channel?.connected ?? false,
1425
+ }));
1426
+ const aunItems = (aidsResponse?.aids ?? []).map((aid) => ({
1427
+ aid: aid.aid,
1428
+ channelName: aid.channelName,
1429
+ status: aid.status ?? 'unknown',
1430
+ connected: aid.status === 'connected',
1431
+ queue: aid.queueStatus ? {
1432
+ pending: aid.queueStatus.queued ?? 0,
1433
+ processing: aid.queueStatus.processing ?? 0,
1434
+ } : null,
1435
+ reconnectCount: aid.reconnectCount ?? 0,
1436
+ }));
1437
+ const host = statusResponse?.host ?? readHostInfo(readLocalPackageVersion());
1438
+ const ecwebEntry = readEcwebInstanceEntries(p)[0];
1439
+ const ecweb = ecwebEntry
1440
+ ? { source: 'instance-record', status: platform.isProcessRunning(ecwebEntry.record.pid) ? 'running' : 'unknown', port: ecwebEntry.record.port, url: `http://localhost:${ecwebEntry.record.port}` }
1441
+ : { source: 'instance-record', status: 'stopped', port: null, url: null };
1442
+ const triggerSummary = statusResponse?.triggers
1443
+ ? { source: 'runtime+history', ...statusResponse.triggers }
1444
+ : { source: 'unavailable', enabled: null, today: { calls: null, failures: null } };
1445
+ const build = statusResponse?.build
1446
+ ? { runtimeMode: statusResponse.build.runtimeMode, version: statusResponse.build.version ?? null }
1447
+ : null;
1448
+ const output = {
1449
+ schemaVersion: 1,
1450
+ generatedAt: new Date(now).toISOString(),
1451
+ timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
1452
+ overall: {
1453
+ status: overallStatus,
1454
+ degraded: !statusAvailable || statusResponse?.agentRuntime?.state === 'error',
1455
+ },
1456
+ host,
1457
+ daemon: {
1458
+ pid: runningPid,
1459
+ uptimeMs: ping?.uptime ?? statusResponse?.uptime ?? null,
1460
+ ipc: { reachable: statusAvailable, latencyMs: Date.now() - started },
1461
+ runMode: statusResponse?.runMode ?? mainRecord?.runMode ?? null,
1462
+ build,
1463
+ queue: statusResponse?.queue ?? { pending: null, processing: null },
1464
+ },
1465
+ baseagents: statusResponse?.baseagents ?? { source: 'unavailable', items: [] },
1466
+ activity: {
1467
+ lastHour: statusActivityJson(effectiveLastHourActivity),
1468
+ today: statusActivityJson(todayActivity),
1469
+ },
1470
+ agents: {
1471
+ source: statusAvailable ? 'daemon+stats-db' : 'stats-db',
1472
+ items: agentItems,
1473
+ },
1474
+ aun: { source: aidsResponse ? 'daemon' : 'unavailable', items: aunItems },
1475
+ channels: { source: statusAvailable ? 'daemon' : 'unavailable', items: channels },
1476
+ controlAid: statusResponse?.controlAid ?? null,
1477
+ triggers: triggerSummary,
1478
+ ecweb,
1479
+ diagnostics,
1480
+ };
1481
+ console.log(JSON.stringify(output, null, 2));
1482
+ }
1483
+ export async function cmdStatus(options = {}) {
1484
+ if (options.format === 'json') {
1485
+ await cmdStatusJson();
1486
+ return;
1487
+ }
1295
1488
  const p = resolvePaths();
1296
1489
  const status = scanInstances();
1297
1490
  const aliveMains = status.mains.filter(m => m.alive);
@@ -1300,13 +1493,19 @@ export async function cmdStatus() {
1300
1493
  // query can otherwise wait behind the same IPC/event-loop delay and make the
1301
1494
  // total `ec status` time the sum of several individual timeouts.
1302
1495
  let ipcFailure;
1496
+ let agentsQueryFailure;
1303
1497
  const pingPromise = probeDaemon(p.socket, DAEMON_PROBE_TIMEOUT_MS, true).catch(error => {
1304
1498
  ipcFailure = error;
1305
1499
  return null;
1306
1500
  });
1307
1501
  const statusPromise = ipcQuery(p.socket, { type: 'status' }).catch(() => null);
1308
1502
  const aidsPromise = ipcQuery(p.socket, { type: 'aun-aids' }).catch(() => null);
1309
- const agentsPromise = ipcQuery(p.socket, { type: 'evolagent.list' }).catch(() => null);
1503
+ const agentsPromise = process.env.EVOLCORE_SESSION_ID
1504
+ ? Promise.resolve({ ok: false, error: 'per-agent listing requires a local control token' })
1505
+ : ipcQuery(p.socket, { type: 'evolagent.list' }, 5_000).catch(error => {
1506
+ agentsQueryFailure = error;
1507
+ return null;
1508
+ });
1310
1509
  // Only the ping is needed to decide whether to show the live-daemon
1311
1510
  // sections. The remaining responses continue in parallel while local
1312
1511
  // process/session information is rendered.
@@ -1484,6 +1683,7 @@ export async function cmdStatus() {
1484
1683
  // Channel status. A running daemon is authoritative and avoids reading
1485
1684
  // protected config files from the CLI's potentially sandboxed process.
1486
1685
  let agentsResponse = null;
1686
+ let liveAgentRuntime = null;
1487
1687
  if (pid) {
1488
1688
  console.log('');
1489
1689
  const [statusResponse, aidsResponse, agentResponse] = await Promise.all([
@@ -1492,6 +1692,7 @@ export async function cmdStatus() {
1492
1692
  agentsPromise,
1493
1693
  ]);
1494
1694
  agentsResponse = agentResponse;
1695
+ liveAgentRuntime = statusResponse?.agentRuntime ?? null;
1495
1696
  if (statusResponse) {
1496
1697
  if (statusResponse.build) {
1497
1698
  if (statusResponse.build.runtimeMode === 'source') {
@@ -1511,6 +1712,16 @@ export async function cmdStatus() {
1511
1712
  console.log(` Build ID: runtime=unavailable (daemon 未提供 build identity) dist=${computeBuildId(path.join(getPackageRoot(), 'dist')) ?? '?'}`);
1512
1713
  console.log(' ⚠ 当前 daemon 未提供 build ID;请重启到包含 build identity 的版本后再判断源码行为。');
1513
1714
  }
1715
+ if (!showLiveChannels(statusResponse)) {
1716
+ const config = readStatusDefaultsConfig(p.defaultsConfig);
1717
+ if (config) {
1718
+ console.log('🔌 Channels (live status unavailable; configuration is stale):');
1719
+ showConfigChannels(config);
1720
+ }
1721
+ else {
1722
+ console.log('🔌 Channels: live status unavailable');
1723
+ }
1724
+ }
1514
1725
  // 🔑 AUN AIDs 表格(详细 AUN 实例状态)
1515
1726
  try {
1516
1727
  if (aidsResponse?.ok && aidsResponse.aids?.length > 0) {
@@ -1558,17 +1769,17 @@ export async function cmdStatus() {
1558
1769
  if (pid) {
1559
1770
  try {
1560
1771
  const agentResult = agentsResponse;
1561
- if (agentResult?.ok && agentResult.agents?.length > 0) {
1772
+ if (agentResult?.ok && Array.isArray(agentResult.agents)) {
1562
1773
  const agents = agentResult.agents;
1563
- if (agents.length > 0) {
1564
- console.log('');
1565
- console.log('🤖 EvolAgents:');
1566
- for (const a of agents) {
1567
- const statusIcon = a.status === 'running' ? '●' : a.status === 'error' ? '✗' : a.status === 'disabled' ? '○' : '◌';
1568
- const channels = summarizeChannelFingerprints(a.channels || []);
1569
- const shortName = a.name.replace(/\.agentid\.pub$/, '');
1570
- console.log(` ${statusIcon} ${shortName.padEnd(20)} ${a.status.padEnd(10)} ${channels}`);
1571
- }
1774
+ console.log('');
1775
+ console.log('🤖 EvolAgents (live status):');
1776
+ if (agents.length === 0)
1777
+ console.log(' (no agents loaded)');
1778
+ for (const a of agents) {
1779
+ const statusIcon = a.status === 'running' ? '●' : a.status === 'error' ? '✗' : a.status === 'disabled' ? '○' : '◌';
1780
+ const channels = summarizeChannelFingerprints(a.channels || []);
1781
+ const shortName = a.name.replace(/\.agentid\.pub$/, '');
1782
+ console.log(` ${statusIcon} ${shortName.padEnd(20)} ${a.status.padEnd(10)} ${channels}`);
1572
1783
  }
1573
1784
  }
1574
1785
  else {
@@ -1578,7 +1789,18 @@ export async function cmdStatus() {
1578
1789
  const agents = diskRegistry.list();
1579
1790
  if (agents.length > 0) {
1580
1791
  console.log('');
1581
- console.log('🤖 EvolAgents (live status unavailable; stale configuration):');
1792
+ const detail = typeof agentResult?.error === 'string'
1793
+ ? agentResult.error
1794
+ : agentsQueryFailure instanceof Error ? agentsQueryFailure.message : 'IPC request failed';
1795
+ const running = Number.isFinite(liveAgentRuntime?.runningAgents) ? liveAgentRuntime.runningAgents : null;
1796
+ const runnable = Number.isFinite(liveAgentRuntime?.runnableAgents) ? liveAgentRuntime.runnableAgents : null;
1797
+ if (running !== null && runnable !== null) {
1798
+ console.log(`🤖 EvolAgents (live aggregate: ${running} running / ${runnable} runnable):`);
1799
+ console.log(` Per-agent details unavailable (${detail}); following entries are stale configuration only:`);
1800
+ }
1801
+ else {
1802
+ console.log(`🤖 EvolAgents (live status unavailable: ${detail}; stale configuration):`);
1803
+ }
1582
1804
  for (const a of agents) {
1583
1805
  const diskStatus = a.status === 'disabled' || a.status === 'error' ? a.status : 'unknown';
1584
1806
  const channels = summarizeChannelFingerprints(a.channels || []);
@@ -2965,7 +3187,7 @@ async function printEcwebStatus(p) {
2965
3187
  console.log('');
2966
3188
  if (aliveEntries.length > 0) {
2967
3189
  const visibleEntry = aliveEntries[0];
2968
- console.log(`🔭 ECWeb: 进程存活 (PID: ${visibleEntry.record.pid}) 但 HTTP 未就绪 (端口 ${visibleEntry.record.port}),查看 logs/watch-web.log`);
3190
+ console.log(`🔭 ECWeb: 进程存活 (PID: ${visibleEntry.record.pid}) 但 HTTP 未就绪 (端口 ${visibleEntry.record.port}),查看 logs/ecweb-daemon.log`);
2969
3191
  }
2970
3192
  else if (occupiedPids.size > 0) {
2971
3193
  console.log(`🔭 ECWeb: 端口仍被未登记进程占用 (PID: ${[...occupiedPids].join(', ')}),请先执行 ec restart`);
@@ -2973,6 +3195,9 @@ async function printEcwebStatus(p) {
2973
3195
  else if (entries.length > 0) {
2974
3196
  console.log('🔭 ECWeb: instance 记录存在,但宿主 PID 不可见且 HTTP 未就绪');
2975
3197
  }
3198
+ else if (externalUrl) {
3199
+ console.log('🔭 ECWeb: 已启用但未运行,查看 logs/ecweb-daemon.log');
3200
+ }
2976
3201
  else {
2977
3202
  console.log('🔭 ECWeb: 未运行');
2978
3203
  }