evolcore 0.0.25 → 0.0.26

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 (231) hide show
  1. package/CHANGELOG.md +607 -565
  2. package/LICENSE +21 -21
  3. package/README.md +455 -455
  4. package/bin/codex-managed-acl.ps1 +160 -160
  5. package/bin/codex-managed-hook.mjs +208 -208
  6. package/bin/ec-safe-output.js +327 -327
  7. package/bin/ec.js +40 -29
  8. package/bin/install-codex-managed-hooks.mjs +582 -555
  9. package/dist/agents/baseagent.js +96 -46
  10. package/dist/agents/claude-runner.js +50 -12
  11. package/dist/agents/codex/app-server-client.js +13 -5
  12. package/dist/agents/codex/catalog.js +1 -1
  13. package/dist/agents/codex/diagnostics.js +34 -1
  14. package/dist/agents/codex-runner.js +121 -38
  15. package/dist/agents/ecagent-runner.js +4 -9
  16. package/dist/agents/gemini-runner.js +2 -7
  17. package/dist/agents/runner-types.js +25 -17
  18. package/dist/aun/aid/agentmd.js +81 -8
  19. package/dist/aun/aid/client.js +1 -1
  20. package/dist/aun/aid/identity.js +1 -1
  21. package/dist/aun/aid/index.js +1 -1
  22. package/dist/aun/aid/store.js +17 -13
  23. package/dist/aun/menu-client.js +74 -0
  24. package/dist/aun/msg/group.js +1 -1
  25. package/dist/aun/msg/managed-operation.js +1 -1
  26. package/dist/aun/msg/p2p.js +6 -1
  27. package/dist/aun/msg/upload.js +30 -22
  28. package/dist/channels/aun.js +329 -94
  29. package/dist/cli/agent-command.js +105 -84
  30. package/dist/cli/agent-create.js +112 -10
  31. package/dist/cli/agent.js +16 -5
  32. package/dist/cli/aun-commands.js +297 -296
  33. package/dist/cli/bench.js +20 -20
  34. package/dist/cli/config.js +47 -28
  35. package/dist/cli/contact.js +70 -13
  36. package/dist/cli/ctl-command.js +22 -22
  37. package/dist/cli/daemon-commands.js +105 -40
  38. package/dist/cli/diag.js +31 -0
  39. package/dist/cli/fs-command.js +67 -67
  40. package/dist/cli/handoff-command.js +41 -9
  41. package/dist/cli/index.js +83 -77
  42. package/dist/cli/link-rules.js +21 -21
  43. package/dist/cli/managed-command-guard.js +3 -0
  44. package/dist/cli/menu-client.js +46 -31
  45. package/dist/cli/net-check.js +22 -22
  46. package/dist/cli/queue-command.js +29 -29
  47. package/dist/cli/response.js +30 -30
  48. package/dist/cli/restart-monitor.js +87 -40
  49. package/dist/cli/stats.js +51 -51
  50. package/dist/cli/trigger-command.js +56 -56
  51. package/dist/config/builtin-roles.js +1 -0
  52. package/dist/config/config-operation-service.js +145 -0
  53. package/dist/config/contact-operation-service.js +96 -7
  54. package/dist/config/resolved-config-op.js +27 -7
  55. package/dist/core/agent-application-service.js +2 -1
  56. package/dist/core/auth/authorization-audit.js +20 -12
  57. package/dist/core/auth/operation-authorizer.js +25 -4
  58. package/dist/core/auth/operation-catalog.js +35 -4
  59. package/dist/core/auth/remote-access-store.js +40 -1
  60. package/dist/core/bootstrap-service.js +1 -1
  61. package/dist/core/command/command-handler.js +83 -26
  62. package/dist/core/command/diag-service.js +118 -0
  63. package/dist/core/command/menu-catalog.js +12 -3
  64. package/dist/core/command/menu-handler.js +47 -26
  65. package/dist/core/command/menu-protocol.js +5 -0
  66. package/dist/core/command/menu-token-store.js +41 -8
  67. package/dist/core/command/slash-gate.js +34 -4
  68. package/dist/core/command/slash-handler.js +86 -21
  69. package/dist/core/handoff/runtime.js +205 -27
  70. package/dist/core/handoff/store.js +193 -4
  71. package/dist/core/message/im-renderer.js +72 -21
  72. package/dist/core/message/message-bridge.js +69 -9
  73. package/dist/core/message/message-queue.js +26 -0
  74. package/dist/core/message/message-utils.js +1 -0
  75. package/dist/core/message/response-engine.js +47 -28
  76. package/dist/core/model/catalog-service.js +79 -49
  77. package/dist/core/model/context-window-suffix.js +33 -0
  78. package/dist/core/model/model-catalog.js +1 -1
  79. package/dist/core/model/model-permission.js +12 -3
  80. package/dist/core/permission/ec-command-parser.js +51 -12
  81. package/dist/core/permission/sandbox-runtime.js +36 -14
  82. package/dist/core/permission/tool-policy.js +2 -1
  83. package/dist/index.js +280 -163
  84. package/dist/ipc.js +54 -2
  85. package/dist/response-system/engines/v1/proactive-flow.js +59 -2
  86. package/dist/stats/billing.js +41 -15
  87. package/dist/stats/db.js +159 -159
  88. package/dist/stats/eck-vars.js +9 -9
  89. package/dist/stats/price-resolver.js +28 -10
  90. package/dist/stats/query.js +139 -139
  91. package/dist/stats/role-budget.js +6 -6
  92. package/dist/stats/writer.js +51 -51
  93. package/dist/utils/cross-platform.js +30 -1
  94. package/dist/utils/error-dict.json +160 -160
  95. package/dist/utils/error-utils.js +21 -0
  96. package/dist/utils/evolcore-version.js +129 -0
  97. package/dist/utils/logger.js +3 -1
  98. package/dist/utils/macos-autostart.js +22 -22
  99. package/dist/utils/model-prices.jsonl +23 -23
  100. package/dist/utils/npm-ops.js +176 -35
  101. package/dist/utils/welcome.js +22 -22
  102. package/ecagent/LICENSE +21 -21
  103. package/ecagent/NOTICE +11 -11
  104. package/ecagent/README.md +61 -61
  105. package/ecagent/dist/harness/compaction/branch-summarization.js +30 -30
  106. package/ecagent/dist/harness/compaction/compaction.js +83 -83
  107. package/ecagent/dist/harness/messages.js +7 -7
  108. package/kits/docs/GUIDE.md +20 -20
  109. package/kits/docs/INDEX.md +66 -66
  110. package/kits/docs/aun/CHEATSHEET.md +19 -19
  111. package/kits/docs/aun/SYNC_PROTOCOL.md +15 -15
  112. package/kits/docs/channels/aun.md +56 -56
  113. package/kits/docs/channels/feishu.md +56 -56
  114. package/kits/docs/context-assembly.md +364 -364
  115. package/kits/docs/eck_templates/GUIDE.template.md +22 -22
  116. package/kits/docs/eck_templates/INDEX.template.md +28 -28
  117. package/kits/docs/eck_templates/path-registry.template.md +33 -33
  118. package/kits/docs/eck_templates/runtime.template.md +19 -19
  119. package/kits/docs/evolcore/INDEX.md +71 -71
  120. package/kits/docs/evolcore/agent-create.md +170 -165
  121. package/kits/docs/evolcore/agent.md +66 -66
  122. package/kits/docs/evolcore/aid.md +60 -60
  123. package/kits/docs/evolcore/codex-windows-sandbox.md +146 -146
  124. package/kits/docs/evolcore/config.md +202 -195
  125. package/kits/docs/evolcore/contact.md +75 -68
  126. package/kits/docs/evolcore/ctl.md +46 -46
  127. package/kits/docs/evolcore/event.md +216 -216
  128. package/kits/docs/evolcore/fs-architecture.md +1215 -1215
  129. package/kits/docs/evolcore/fs.md +110 -110
  130. package/kits/docs/evolcore/group-collaboration.md +243 -243
  131. package/kits/docs/evolcore/group-fs.md +17 -17
  132. package/kits/docs/evolcore/group-rules.md +251 -251
  133. package/kits/docs/evolcore/group.md +152 -152
  134. package/kits/docs/evolcore/menu-client.md +42 -34
  135. package/kits/docs/evolcore/model.md +50 -50
  136. package/kits/docs/evolcore/msg.md +166 -165
  137. package/kits/docs/evolcore/response.md +75 -75
  138. package/kits/docs/evolcore/rpc.md +37 -37
  139. package/kits/docs/evolcore/self-summary.md +29 -29
  140. package/kits/docs/evolcore/stats.md +83 -83
  141. package/kits/docs/evolcore/storage.md +50 -50
  142. package/kits/docs/evolcore/trigger.md +575 -575
  143. package/kits/docs/identity/AID_PROFILE_SPEC.md +26 -26
  144. package/kits/docs/identity/PATH_OPS.md +16 -16
  145. package/kits/docs/identity/ROLE_DETAIL.md +25 -25
  146. package/kits/docs/identity/identity-tools.md +26 -26
  147. package/kits/docs/path-registry.md +62 -62
  148. package/kits/docs/prompt-loading-architecture.md +269 -269
  149. package/kits/docs/venues/aun-group.md +45 -45
  150. package/kits/docs/venues/aun-private.md +10 -10
  151. package/kits/docs/venues/client-desktop.md +10 -10
  152. package/kits/docs/venues/client-mobile.md +10 -10
  153. package/kits/docs/venues/feishu-group.md +13 -13
  154. package/kits/docs/venues/feishu-private.md +9 -9
  155. package/kits/docs/venues/group.md +37 -25
  156. package/kits/docs/venues/private.md +10 -10
  157. package/kits/eck_manifest.auxiliary.json +43 -43
  158. package/kits/eck_manifest.json +190 -190
  159. package/kits/eck_message_manifest.json +74 -74
  160. package/kits/migrations/README-role-config-v4.md +32 -32
  161. package/kits/migrations/migrate-contact-book-v2.mjs +748 -748
  162. package/kits/migrations/migrate-role-config-v4.mjs +614 -614
  163. package/kits/migrations/migrate-role-config-v5.mjs +365 -365
  164. package/kits/migrations/rename-config-file.mjs +99 -99
  165. package/kits/rules/01-overview.md +162 -162
  166. package/kits/rules/02-navigation.md +96 -96
  167. package/kits/rules/03-identity.md +36 -36
  168. package/kits/rules/04-relation.md +75 -75
  169. package/kits/rules/05-venue.md +60 -60
  170. package/kits/rules/06-channel.md +63 -63
  171. package/kits/schemas/_meta.json +53 -53
  172. package/kits/schemas/agent-config.schema.1.json +177 -177
  173. package/kits/schemas/agent-config.schema.10.json +395 -395
  174. package/kits/schemas/agent-config.schema.11.json +421 -421
  175. package/kits/schemas/agent-config.schema.12.json +427 -427
  176. package/kits/schemas/agent-config.schema.2.json +239 -239
  177. package/kits/schemas/agent-config.schema.3.json +119 -119
  178. package/kits/schemas/agent-config.schema.4.json +208 -208
  179. package/kits/schemas/agent-config.schema.5.json +326 -326
  180. package/kits/schemas/agent-config.schema.6.json +322 -322
  181. package/kits/schemas/agent-config.schema.7.json +303 -303
  182. package/kits/schemas/agent-config.schema.8.json +304 -304
  183. package/kits/schemas/agent-config.schema.9.json +364 -364
  184. package/kits/schemas/contact-book.schema.1.json +36 -36
  185. package/kits/schemas/contact-book.schema.2.json +43 -43
  186. package/kits/schemas/contact-book.schema.3.json +67 -67
  187. package/kits/schemas/daemon.schema.1.json +91 -91
  188. package/kits/schemas/daemon.schema.2.json +101 -101
  189. package/kits/schemas/daemon.schema.3.json +123 -123
  190. package/kits/schemas/daemon.schema.4.json +136 -136
  191. package/kits/schemas/daemon.schema.5.json +135 -135
  192. package/kits/schemas/daemon.schema.6.json +131 -131
  193. package/kits/schemas/defaults.schema.1.json +81 -81
  194. package/kits/schemas/defaults.schema.2.json +85 -85
  195. package/kits/schemas/defaults.schema.3.json +73 -73
  196. package/kits/schemas/defaults.schema.4.json +107 -107
  197. package/kits/schemas/defaults.schema.5.json +119 -119
  198. package/kits/schemas/menu-exec-schema-commands.md +208 -208
  199. package/kits/schemas/migrations/README.md +30 -30
  200. package/kits/schemas/relation-config.schema.1.json +158 -158
  201. package/kits/schemas/relation-config.schema.2.json +73 -73
  202. package/kits/schemas/relation-config.schema.3.json +50 -50
  203. package/kits/schemas/relation-config.schema.4.json +47 -47
  204. package/kits/schemas/relation-config.schema.5.json +47 -47
  205. package/kits/schemas/relation-config.schema.6.json +46 -46
  206. package/kits/schemas/relation-config.schema.7.json +59 -59
  207. package/kits/schemas/relation-config.schema.8.json +103 -103
  208. package/kits/schemas/role-config.schema.1.json +201 -201
  209. package/kits/schemas/role-registry.schema.1.json +35 -35
  210. package/kits/schemas/single-session.schema.1.json +31 -31
  211. package/kits/schemas/single-session.schema.2.json +57 -57
  212. package/kits/schemas/single-session.schema.3.json +32 -32
  213. package/kits/templates/bootstrap-welcome.md +17 -17
  214. package/kits/templates/message-fragments/handoff-context-to-target.md +9 -9
  215. package/kits/templates/message-fragments/handoff-request-to-target.md +19 -19
  216. package/kits/templates/message-fragments/handoff-response-to-origin.md +9 -9
  217. package/kits/templates/message-fragments/inject-default.md +2 -2
  218. package/kits/templates/message-fragments/item.md +2 -2
  219. package/kits/templates/roles/admin.json +92 -92
  220. package/kits/templates/roles/member.json +96 -96
  221. package/kits/templates/roles/owner.json +9 -9
  222. package/kits/templates/roles/visitor.json +85 -85
  223. package/kits/templates/system-fragments/baseagent.md +14 -14
  224. package/kits/templates/system-fragments/bootstrap.md +31 -29
  225. package/kits/templates/system-fragments/channel.md +64 -64
  226. package/kits/templates/system-fragments/commands.md +28 -28
  227. package/kits/templates/system-fragments/identity.md +13 -13
  228. package/kits/templates/system-fragments/relation.md +19 -19
  229. package/kits/templates/system-fragments/session.md +59 -59
  230. package/kits/templates/system-fragments/venue.md +31 -31
  231. package/package.json +100 -98
package/dist/index.js CHANGED
@@ -46,7 +46,7 @@ import { MessageQueue } from './core/message/message-queue.js';
46
46
  import { MessageBridge } from './core/message/message-bridge.js';
47
47
  import { evaluateOutboundContactAdmission } from './core/message/message-admission.js';
48
48
  import { enforceMenuResponseSize, evaluateMenuClientCompatibility, evolMenuResponseTransportMetadata, MenuRequestDeduper, hasValidMenuId, menuFailure, menuPayloadFingerprint, normalizeMenuResponseTiming, parseMenuControl, validateMenuRequest, withMenuProcessingTime, logMenuRequestCompleted, logMenuRequestReceived } from './core/command/menu-protocol.js';
49
- import { readInstalledEvolcoreVersion } from './utils/evolcore-version.js';
49
+ import { computeBuildId, readBuildIdentity, readEvolcoreRuntimeFlavor, readEvolcoreStartedAtIso, readInstalledEvolcoreVersion, tagErrorTextWithEvolcoreVersion, } from './utils/evolcore-version.js';
50
50
  import { recoverAllRoleMutationsSync } from './core/command/role-menu.js';
51
51
  import { HandoffRuntime } from './core/handoff/runtime.js';
52
52
  import { BootstrapService, completeBootstrapWithWelcome } from './core/bootstrap-service.js';
@@ -75,6 +75,7 @@ import { buildReloadHooks } from './core/channel-loader.js';
75
75
  import { IpcServer } from './ipc.js';
76
76
  import { logger, setLogLevel } from './utils/logger.js';
77
77
  import { remoteAccessService } from './core/auth/remote-access-store.js';
78
+ import { DiagService, executeDiagIpc } from './core/command/diag-service.js';
78
79
  import { createEcwebSupervisor } from './utils/ecweb-supervisor.js';
79
80
  import { writeMain, removeAll, isMainWinner, scanInstances } from './utils/instance-registry.js';
80
81
  import { detectDuplicates } from './core/evolagent-registry.js';
@@ -147,6 +148,45 @@ function summarizeOutboundPayload(payload) {
147
148
  }
148
149
  return s;
149
150
  }
151
+ /**
152
+ * 给用户可见的错误文案打上 EvolCore 版本标记,便于从对端截图直接判断版本与运行形态。
153
+ *
154
+ * 打标范围只含"出错了"语义的 payload:
155
+ * - `result.error` / `system.error`:任务级与系统级失败(含沙箱、权限、超时)
156
+ * - `status.error`:终态元数据,附带结构化版本字段供前端不受截断影响地展示
157
+ * - `command.error`:仅当带 `reasonCode`(授权/权限拒绝)时才打。命令参数写错、
158
+ * 未知命令这类输入校验反馈不打——它们与版本无关,逐条加前缀只会变成噪音。
159
+ *
160
+ * 返回新对象,不改调用方持有的 payload(同一 payload 可能被重复发送到多个渠道)。
161
+ */
162
+ function tagErrorPayloadWithEvolcoreVersion(payload) {
163
+ switch (payload.kind) {
164
+ case 'result.error':
165
+ case 'system.error':
166
+ return { ...payload, text: tagErrorTextWithEvolcoreVersion(payload.text) };
167
+ case 'command.error':
168
+ if (!payload.reasonCode)
169
+ return payload;
170
+ return { ...payload, text: tagErrorTextWithEvolcoreVersion(payload.text) };
171
+ case 'status.error': {
172
+ const metadata = payload.metadata;
173
+ return {
174
+ ...payload,
175
+ metadata: {
176
+ ...metadata,
177
+ ...(typeof metadata?.message === 'string'
178
+ ? { message: tagErrorTextWithEvolcoreVersion(metadata.message) }
179
+ : {}),
180
+ evolcoreVersion: readInstalledEvolcoreVersion(),
181
+ evolcoreFlavor: readEvolcoreRuntimeFlavor(),
182
+ evolcoreStartedAt: readEvolcoreStartedAtIso(),
183
+ },
184
+ };
185
+ }
186
+ default:
187
+ return payload;
188
+ }
189
+ }
150
190
  function shouldCountSentPayload(payload) {
151
191
  return [
152
192
  'result.text',
@@ -333,153 +373,153 @@ function seedUpgradeCheckTrigger(manager, owner) {
333
373
  // take the watchdog down with it.
334
374
  const monitorEntry = platform.resolveCliEntry(packageRoot);
335
375
  const stageParent = path.join(resolvePaths().root, 'data', 'monitor-stage');
336
- fs.writeFileSync(scriptPath, `#!/usr/bin/env node
337
- const fs = require('node:fs');
338
- const path = require('node:path');
339
- const { existsSync, readFileSync } = fs;
340
- const { spawn } = require('node:child_process');
341
-
342
- const CLI_PATH = ${JSON.stringify(monitorEntry.entry)};
343
- const CLI_ARGS = ${JSON.stringify(monitorEntry.args)};
344
- const PACKAGE_ROOT = ${JSON.stringify(packageRoot)};
345
- const STAGE_PARENT = ${JSON.stringify(stageParent)};
346
- const PACKAGE_JSON = ${JSON.stringify(packageJsonPath)};
347
- const PACKAGE_NAME = ${JSON.stringify(MAIN_PACKAGE_NAME)};
348
- const DEV_MODE = ${devMode ? 'true' : 'false'};
349
-
350
- function emit(outcome, text) {
351
- process.stdout.write(JSON.stringify({ outcome, text, files: [] }));
352
- }
353
-
354
- function compareVersions(a, b) {
355
- const pa = a.split('-')[0].split('.').map(Number);
356
- const pb = b.split('-')[0].split('.').map(Number);
357
- const count = Math.max(pa.length, pb.length);
358
- for (let i = 0; i < count; i += 1) {
359
- const av = Number.isFinite(pa[i]) ? pa[i] : 0;
360
- const bv = Number.isFinite(pb[i]) ? pb[i] : 0;
361
- if (av < bv) return -1;
362
- if (av > bv) return 1;
363
- }
364
- return 0;
365
- }
366
-
367
- async function latestVersion() {
368
- const controller = new AbortController();
369
- const timer = setTimeout(() => controller.abort(), 10_000);
370
- try {
371
- const response = await fetch('https://registry.npmjs.org/' + encodeURIComponent(PACKAGE_NAME) + '/latest', {
372
- signal: controller.signal,
373
- headers: { Accept: 'application/json' },
374
- });
375
- if (!response.ok) return null;
376
- const data = await response.json();
377
- return typeof data.version === 'string' ? data.version : null;
378
- } finally {
379
- clearTimeout(timer);
380
- }
381
- }
382
-
383
- async function main() {
384
- if (DEV_MODE) {
385
- emit('noop', 'upgrade check skipped in dev mode');
386
- return;
387
- }
388
-
389
- let localVersion;
390
- try {
391
- localVersion = JSON.parse(readFileSync(PACKAGE_JSON, 'utf8')).version;
392
- } catch {
393
- emit('error', 'failed to read local ec version');
394
- return;
395
- }
396
- if (typeof localVersion !== 'string' || !localVersion) {
397
- emit('error', 'failed to read local ec version');
398
- return;
399
- }
400
-
401
- let remoteVersion;
402
- try {
403
- remoteVersion = await latestVersion();
404
- } catch {
405
- remoteVersion = null;
406
- }
407
- if (!remoteVersion) {
408
- emit('error', 'failed to check evolcore latest version');
409
- return;
410
- }
411
- if (compareVersions(localVersion, remoteVersion) >= 0) {
412
- emit('noop', 'evolcore is already up to date (' + localVersion + '; latest ' + remoteVersion + ')');
413
- return;
414
- }
415
- if (!existsSync(CLI_PATH)) {
416
- emit('error', 'restart-monitor entry not found: ' + CLI_PATH);
417
- return;
418
- }
419
-
420
- // Installed mode: copy the monitor out of the package before it upgrades that
421
- // same package. Mirrors prepareMonitorStage(); kept inline because this file
422
- // is generated standalone CJS. Any failure degrades to launching in place —
423
- // losing upgrade isolation beats losing the upgrade.
424
- let launchArgs = CLI_ARGS;
425
- const launchEnv = { ...process.env };
426
- if (!DEV_MODE && STAGE_PARENT) {
427
- try {
428
- const stageDir = path.join(STAGE_PARENT, 'upgrade-' + process.pid + '-' + Date.now());
429
- fs.mkdirSync(stageDir, { recursive: true });
430
- fs.cpSync(path.join(PACKAGE_ROOT, 'dist'), path.join(stageDir, 'dist'), { recursive: true });
431
- if (existsSync(PACKAGE_JSON)) fs.copyFileSync(PACKAGE_JSON, path.join(stageDir, 'package.json'));
432
- const modulesSource = path.join(PACKAGE_ROOT, 'node_modules');
433
- if (existsSync(modulesSource)) {
434
- const staged = path.join(stageDir, 'node_modules');
435
- try {
436
- // junction works unelevated on Windows; dir symlink elsewhere.
437
- fs.symlinkSync(modulesSource, staged, process.platform === 'win32' ? 'junction' : 'dir');
438
- fs.readdirSync(staged);
439
- } catch (linkError) {
440
- fs.rmSync(staged, { recursive: true, force: true });
441
- fs.cpSync(modulesSource, staged, { recursive: true });
442
- }
443
- }
444
- const stagedEntry = path.join(stageDir, 'dist', 'cli', 'index.js');
445
- if (existsSync(stagedEntry)) {
446
- launchArgs = [stagedEntry];
447
- launchEnv.EVOLCORE_TARGET_PACKAGE_ROOT = PACKAGE_ROOT;
448
- launchEnv.EVOLCORE_MONITOR_STAGE = stageDir;
449
- }
450
- } catch (stageError) {
451
- // Fall through with the in-place entry.
452
- }
453
- }
454
-
455
- try {
456
- const child = spawn(process.execPath, [...launchArgs, 'restart-monitor'], {
457
- detached: true,
458
- stdio: 'ignore',
459
- windowsHide: process.platform === 'win32',
460
- env: launchEnv,
461
- });
462
- child.unref();
463
- const pid = child.pid;
464
- // The daemon is still running here — the monitor is what stops it. So a
465
- // monitor that dies on load costs the upgrade, not the service; still,
466
- // report it accurately instead of claiming success.
467
- await new Promise(resolve => setTimeout(resolve, 3000));
468
- let alive = false;
469
- try { process.kill(pid, 0); alive = true; } catch (e) { alive = false; }
470
- if (!alive) {
471
- emit('error', 'restart-monitor exited during startup; upgrade to ' + remoteVersion + ' not applied');
472
- return;
473
- }
474
- emit('success', 'evolcore upgrade available: ' + localVersion + ' -> ' + remoteVersion + '; restart-monitor started');
475
- } catch (error) {
476
- emit('error', 'failed to start restart-monitor: ' + (error && error.message ? error.message : String(error)));
477
- }
478
- }
479
-
480
- main().catch((error) => {
481
- emit('error', 'upgrade check failed: ' + (error && error.message ? error.message : String(error)));
482
- });
376
+ fs.writeFileSync(scriptPath, `#!/usr/bin/env node
377
+ const fs = require('node:fs');
378
+ const path = require('node:path');
379
+ const { existsSync, readFileSync } = fs;
380
+ const { spawn } = require('node:child_process');
381
+
382
+ const CLI_PATH = ${JSON.stringify(monitorEntry.entry)};
383
+ const CLI_ARGS = ${JSON.stringify(monitorEntry.args)};
384
+ const PACKAGE_ROOT = ${JSON.stringify(packageRoot)};
385
+ const STAGE_PARENT = ${JSON.stringify(stageParent)};
386
+ const PACKAGE_JSON = ${JSON.stringify(packageJsonPath)};
387
+ const PACKAGE_NAME = ${JSON.stringify(MAIN_PACKAGE_NAME)};
388
+ const DEV_MODE = ${devMode ? 'true' : 'false'};
389
+
390
+ function emit(outcome, text) {
391
+ process.stdout.write(JSON.stringify({ outcome, text, files: [] }));
392
+ }
393
+
394
+ function compareVersions(a, b) {
395
+ const pa = a.split('-')[0].split('.').map(Number);
396
+ const pb = b.split('-')[0].split('.').map(Number);
397
+ const count = Math.max(pa.length, pb.length);
398
+ for (let i = 0; i < count; i += 1) {
399
+ const av = Number.isFinite(pa[i]) ? pa[i] : 0;
400
+ const bv = Number.isFinite(pb[i]) ? pb[i] : 0;
401
+ if (av < bv) return -1;
402
+ if (av > bv) return 1;
403
+ }
404
+ return 0;
405
+ }
406
+
407
+ async function latestVersion() {
408
+ const controller = new AbortController();
409
+ const timer = setTimeout(() => controller.abort(), 10_000);
410
+ try {
411
+ const response = await fetch('https://registry.npmjs.org/' + encodeURIComponent(PACKAGE_NAME) + '/latest', {
412
+ signal: controller.signal,
413
+ headers: { Accept: 'application/json' },
414
+ });
415
+ if (!response.ok) return null;
416
+ const data = await response.json();
417
+ return typeof data.version === 'string' ? data.version : null;
418
+ } finally {
419
+ clearTimeout(timer);
420
+ }
421
+ }
422
+
423
+ async function main() {
424
+ if (DEV_MODE) {
425
+ emit('noop', 'upgrade check skipped in dev mode');
426
+ return;
427
+ }
428
+
429
+ let localVersion;
430
+ try {
431
+ localVersion = JSON.parse(readFileSync(PACKAGE_JSON, 'utf8')).version;
432
+ } catch {
433
+ emit('error', 'failed to read local ec version');
434
+ return;
435
+ }
436
+ if (typeof localVersion !== 'string' || !localVersion) {
437
+ emit('error', 'failed to read local ec version');
438
+ return;
439
+ }
440
+
441
+ let remoteVersion;
442
+ try {
443
+ remoteVersion = await latestVersion();
444
+ } catch {
445
+ remoteVersion = null;
446
+ }
447
+ if (!remoteVersion) {
448
+ emit('error', 'failed to check evolcore latest version');
449
+ return;
450
+ }
451
+ if (compareVersions(localVersion, remoteVersion) >= 0) {
452
+ emit('noop', 'evolcore is already up to date (' + localVersion + '; latest ' + remoteVersion + ')');
453
+ return;
454
+ }
455
+ if (!existsSync(CLI_PATH)) {
456
+ emit('error', 'restart-monitor entry not found: ' + CLI_PATH);
457
+ return;
458
+ }
459
+
460
+ // Installed mode: copy the monitor out of the package before it upgrades that
461
+ // same package. Mirrors prepareMonitorStage(); kept inline because this file
462
+ // is generated standalone CJS. Any failure degrades to launching in place —
463
+ // losing upgrade isolation beats losing the upgrade.
464
+ let launchArgs = CLI_ARGS;
465
+ const launchEnv = { ...process.env };
466
+ if (!DEV_MODE && STAGE_PARENT) {
467
+ try {
468
+ const stageDir = path.join(STAGE_PARENT, 'upgrade-' + process.pid + '-' + Date.now());
469
+ fs.mkdirSync(stageDir, { recursive: true });
470
+ fs.cpSync(path.join(PACKAGE_ROOT, 'dist'), path.join(stageDir, 'dist'), { recursive: true });
471
+ if (existsSync(PACKAGE_JSON)) fs.copyFileSync(PACKAGE_JSON, path.join(stageDir, 'package.json'));
472
+ const modulesSource = path.join(PACKAGE_ROOT, 'node_modules');
473
+ if (existsSync(modulesSource)) {
474
+ const staged = path.join(stageDir, 'node_modules');
475
+ try {
476
+ // junction works unelevated on Windows; dir symlink elsewhere.
477
+ fs.symlinkSync(modulesSource, staged, process.platform === 'win32' ? 'junction' : 'dir');
478
+ fs.readdirSync(staged);
479
+ } catch (linkError) {
480
+ fs.rmSync(staged, { recursive: true, force: true });
481
+ fs.cpSync(modulesSource, staged, { recursive: true });
482
+ }
483
+ }
484
+ const stagedEntry = path.join(stageDir, 'dist', 'cli', 'index.js');
485
+ if (existsSync(stagedEntry)) {
486
+ launchArgs = [stagedEntry];
487
+ launchEnv.EVOLCORE_TARGET_PACKAGE_ROOT = PACKAGE_ROOT;
488
+ launchEnv.EVOLCORE_MONITOR_STAGE = stageDir;
489
+ }
490
+ } catch (stageError) {
491
+ // Fall through with the in-place entry.
492
+ }
493
+ }
494
+
495
+ try {
496
+ const child = spawn(process.execPath, [...launchArgs, 'restart-monitor'], {
497
+ detached: true,
498
+ stdio: 'ignore',
499
+ windowsHide: process.platform === 'win32',
500
+ env: launchEnv,
501
+ });
502
+ child.unref();
503
+ const pid = child.pid;
504
+ // The daemon is still running here — the monitor is what stops it. So a
505
+ // monitor that dies on load costs the upgrade, not the service; still,
506
+ // report it accurately instead of claiming success.
507
+ await new Promise(resolve => setTimeout(resolve, 3000));
508
+ let alive = false;
509
+ try { process.kill(pid, 0); alive = true; } catch (e) { alive = false; }
510
+ if (!alive) {
511
+ emit('error', 'restart-monitor exited during startup; upgrade to ' + remoteVersion + ' not applied');
512
+ return;
513
+ }
514
+ emit('success', 'evolcore upgrade available: ' + localVersion + ' -> ' + remoteVersion + '; restart-monitor started');
515
+ } catch (error) {
516
+ emit('error', 'failed to start restart-monitor: ' + (error && error.message ? error.message : String(error)));
517
+ }
518
+ }
519
+
520
+ main().catch((error) => {
521
+ emit('error', 'upgrade check failed: ' + (error && error.message ? error.message : String(error)));
522
+ });
483
523
  `);
484
524
  // Remove the pre-Node script left by older installations so it cannot be
485
525
  // selected by a stale trigger definition or run manually by accident.
@@ -610,6 +650,7 @@ async function runBindBootstrapDaemon(daemonCfg) {
610
650
  }));
611
651
  ipcServer.setControlToken(controlToken);
612
652
  ipcServer.setRemoteAccessService(remoteAccessService);
653
+ ipcServer.setRemoteAccessOwnerProvider(() => daemonCfg.owners?.[0]);
613
654
  ipcServer.setShutdownExecutor(async (reason) => {
614
655
  if (!shutdown) {
615
656
  pendingShutdownReason = reason;
@@ -757,6 +798,12 @@ async function main() {
757
798
  // Service managers do not necessarily export TMPDIR. Establish the private
758
799
  // managed root before any runner or task runtime is initialized.
759
800
  ensureProcessManagedTempDir();
801
+ const runtimeEntry = fileURLToPath(import.meta.url);
802
+ const diskDistDir = path.join(getPackageRoot(), 'dist');
803
+ const runtimeIsDist = path.resolve(path.dirname(runtimeEntry)) === path.resolve(diskDistDir);
804
+ const runtimeBuildIdentity = readBuildIdentity(runtimeIsDist ? path.dirname(runtimeEntry) : diskDistDir, readEvolcoreVersion());
805
+ if (!runtimeIsDist)
806
+ runtimeBuildIdentity.id = null;
760
807
  // 启动信息:目录类型 + 版本号 + 代码最新时间戳
761
808
  {
762
809
  const pkgRoot = getPackageRoot();
@@ -798,6 +845,10 @@ async function main() {
798
845
  console.error(`[EvolCore] 代码时间: ${latestMtime ? fmtTime(latestMtime) : '?'}`);
799
846
  }
800
847
  console.error('[EvolCore] ✓ Version info printed');
848
+ const diskBuildId = computeBuildId(diskDistDir);
849
+ console.error(`[EvolCore] 运行 build ID: ${runtimeIsDist ? (runtimeBuildIdentity.id ?? '?') : 'source'}`);
850
+ console.error(`[EvolCore] 磁盘 dist build ID: ${diskBuildId ?? '?'}`);
851
+ logger.info(`[Build] runtimeMode=${runtimeIsDist ? 'dist' : 'source'} runtimeBuildId=${runtimeBuildIdentity.id ?? 'source'} diskDistBuildId=${diskBuildId ?? 'unknown'} match=${runtimeIsDist && runtimeBuildIdentity.id !== null && runtimeBuildIdentity.id === diskBuildId}`);
801
852
  // 过滤飞书 SDK 的 info 日志
802
853
  const originalLog = console.log;
803
854
  const originalInfo = console.info;
@@ -975,13 +1026,13 @@ async function main() {
975
1026
  port: ecwebConfig.port ?? DEFAULT_ECWEB_PORT,
976
1027
  logger,
977
1028
  });
978
- try {
979
- await ecwebSupervisor.start();
980
- onShutdown(() => ecwebSupervisor?.stop());
981
- }
982
- catch (error) {
1029
+ // ECWeb is optional and has its own health checks/restart loop. Starting
1030
+ // it must not hold up the daemon's agent/IPC initialization or ready
1031
+ // signal; status will report it as starting until the first healthy probe.
1032
+ onShutdown(() => ecwebSupervisor?.stop());
1033
+ void ecwebSupervisor.start().catch((error) => {
983
1034
  logger.warn(`ECWeb supervisor 启动失败(不影响 daemon 主流程): ${error?.message || error}`);
984
- }
1035
+ });
985
1036
  }
986
1037
  if (bindBootstrapMode && daemonCfg.aid && loadAllAgents().agents.length === 0) {
987
1038
  await runBindBootstrapDaemon(daemonCfg);
@@ -1918,7 +1969,9 @@ async function main() {
1918
1969
  function registerChannelInstance(inst) {
1919
1970
  // 0. 包装 adapter.send,记录所有出站到 channel-out.log
1920
1971
  const originalSend = inst.adapter.send.bind(inst.adapter);
1921
- inst.adapter.send = async (envelope, payload) => {
1972
+ inst.adapter.send = async (envelope, rawPayload) => {
1973
+ // 版本标记在记账之前打,让 channel-out.log 与对端看到的文案一致。
1974
+ const payload = rawPayload ? tagErrorPayloadWithEvolcoreVersion(rawPayload) : rawPayload;
1922
1975
  const owningAgent = agentRegistry.resolveByChannel(inst.adapter.channelKey)
1923
1976
  ?? agentRegistry.resolveByChannel(inst.adapter.channelName);
1924
1977
  logger.channelOut({
@@ -2316,6 +2369,14 @@ async function main() {
2316
2369
  // A failed bind (for example, a stale Windows named pipe) must abort startup
2317
2370
  // without creating a second AUN session that can kick the old daemon.
2318
2371
  let controlChannel;
2372
+ const diagService = new DiagService(() => ({ controlAid: loadDaemonConfig().aid, owners: loadDaemonConfig().owners ?? [] }), async (to, text) => {
2373
+ if (!controlChannel)
2374
+ throw new Error('Control AID channel is unavailable');
2375
+ const result = await controlChannel.sendDaemonMsg({ to, payload: { type: 'text', text }, encrypt: true });
2376
+ if (!result.ok)
2377
+ throw new Error(result.error ?? 'Invitation send failed');
2378
+ });
2379
+ cmdHandler.setDiagService(diagService);
2319
2380
  let shutdown;
2320
2381
  let pendingShutdownReason;
2321
2382
  const ipcServer = new IpcServer(resolvePaths().socket, () => {
@@ -2335,6 +2396,13 @@ async function main() {
2335
2396
  return {
2336
2397
  pid: process.pid,
2337
2398
  uptime: snap.uptimeMs,
2399
+ build: {
2400
+ runtimeMode: runtimeIsDist ? 'dist' : 'source',
2401
+ runtimeId: runtimeBuildIdentity.id,
2402
+ diskDistId: computeBuildId(diskDistDir),
2403
+ entry: runtimeBuildIdentity.entry,
2404
+ version: runtimeBuildIdentity.version,
2405
+ },
2338
2406
  controlPlane: {
2339
2407
  ready: true,
2340
2408
  owned: processLevelOwners.length > 0,
@@ -2384,6 +2452,7 @@ async function main() {
2384
2452
  return cmdHandler.handleCtl(cmd, sessionId, delegation.grant);
2385
2453
  });
2386
2454
  ipcServer.setDaemonRuntimeEpoch(agentDelegationRegistry.getRuntimeEpoch());
2455
+ ipcServer.setRemoteAccessOwnerProvider(() => processLevelOwners[0]);
2387
2456
  cmdHandler.setDaemonMonitorProvider(() => ipcServer.getMonitorSnapshot().snapshot);
2388
2457
  // Register every IPC executor/provider before exposing the endpoint. The
2389
2458
  // function declaration is hoisted, while its invocation remains here so a
@@ -2446,14 +2515,16 @@ async function main() {
2446
2515
  aunSdkLog: daemonCfg.debug?.aunSdkLog,
2447
2516
  });
2448
2517
  // connect() 失败不置空实例:AUNChannel 内部有无限重连(SDK auto_reconnect +
2449
- // scheduleReconnect),首连失败后台会自愈;保留实例供 status 显示 disconnected。
2450
- try {
2451
- await controlChannel.connect();
2518
+ // scheduleReconnect),首连失败会自愈;启动连接放到后台,避免 ready signal
2519
+ // 被远端网关/TLS 首连延迟阻塞。Attach the handlers below before the
2520
+ // promise can complete so an early control message is still accepted.
2521
+ const controlConnectPromise = controlChannel.connect()
2522
+ .then(() => {
2452
2523
  logger.info(`✓ 控制 AID 已连接: ${daemonCfg.aid}`);
2453
- }
2454
- catch (e) {
2524
+ })
2525
+ .catch((e) => {
2455
2526
  logger.warn(`控制 AID 首连失败(后台自动重连,不影响 daemon 主流程): ${e?.message || e}`);
2456
- }
2527
+ });
2457
2528
  // 控制 AID 接收 daemon-owner 或有效远程访问 token 授权的指令:
2458
2529
  // 1. /pair — daemon 远程控制配对码(文本快路径)
2459
2530
  // 2. menu.* JSON — 路由到 cmdHandler.execMenuForControl(进程级 + 全量权限)
@@ -2522,6 +2593,13 @@ async function main() {
2522
2593
  const isRoleMenu = menuControl.isMenu
2523
2594
  && (menuControl.name === 'role' || menuControl.request.cmd === '/role');
2524
2595
  const currentProcessOwners = loadDaemonConfig().owners ?? [];
2596
+ if (/^\/diag(?:\s|$)/i.test(text)) {
2597
+ const command = text.slice(5).trim();
2598
+ const result = await diagService.execute(command.toLowerCase() === 'stop'
2599
+ ? { action: 'stop' } : { action: 'start', problem: command }, opts.peerId);
2600
+ await controlChannel.sendMessage(opts.channelId, result.message, controlReplyContext(opts));
2601
+ return;
2602
+ }
2525
2603
  if (menuControl.isMenu && !hasValidMenuId(menuControl)) {
2526
2604
  logger.warn(`[ControlMenu] dropped malformed request without id type=${menuControl.type}`);
2527
2605
  logMenuRequestCompleted(menuControl.request, undefined, menuFlowContext, {
@@ -2595,7 +2673,7 @@ async function main() {
2595
2673
  return;
2596
2674
  }
2597
2675
  // owner 发的其他内容:提示可用指令
2598
- await controlChannel.sendMessage(opts.channelId, '可用指令:/pair(获取远程控制配对码)', controlReplyContext(opts));
2676
+ await controlChannel.sendMessage(opts.channelId, '可用指令:/pair(获取远程控制配对码)、/diag <问题描述>、/diag stop', controlReplyContext(opts));
2599
2677
  }
2600
2678
  catch (e) {
2601
2679
  if (activeMenuRequest && !menuCompletionLogged) {
@@ -2608,6 +2686,7 @@ async function main() {
2608
2686
  logger.warn(`控制 AID 消息处理失败: ${e?.message || e}`);
2609
2687
  }
2610
2688
  });
2689
+ void controlConnectPromise;
2611
2690
  // ── Service Proxy:把本地服务(ecweb 等)通过控制 AID 暴露到 AUN 网络 ──
2612
2691
  // 挂在控制 AUNChannel 上,动态解引用其 client(规避重连换 client)。
2613
2692
  // 失败只 warn,不影响 daemon 主流程。
@@ -2859,6 +2938,17 @@ async function main() {
2859
2938
  ipcServer.setConfigOperationExecutor((argv, sessionId, delegationToken, delegationCommandHash) => cmdHandler.handleConfigOperation(argv, sessionId, delegationToken, delegationCommandHash));
2860
2939
  ipcServer.setContactOperationExecutor((argv, sessionId, delegationToken, delegationCommandHash) => cmdHandler.handleContactOperation(argv, sessionId, delegationToken, delegationCommandHash));
2861
2940
  ipcServer.setAidOperationExecutor((argv, sessionId, delegationToken, delegationCommandHash) => cmdHandler.handleAidOperation(argv, sessionId, delegationToken, delegationCommandHash));
2941
+ ipcServer.setDiagExecutor(cmd => executeDiagIpc(diagService, agentDelegationRegistry, loadDaemonConfig().owners ?? [], cmd));
2942
+ ipcServer.setMenuClientExecutor(async (cmd) => {
2943
+ if (!Array.isArray(cmd.argv) || cmd.argv.some((v) => typeof v !== 'string') || typeof cmd.sessionId !== 'string')
2944
+ return { ok: false, code: 'INVALID_ARGUMENT', error: 'A managed session and Menu argv are required' };
2945
+ return cmdHandler.handleMenuClientOperation(cmd.argv, cmd.sessionId, cmd.delegationToken, cmd.delegationCommandHash, async (options) => {
2946
+ const instance = channelInstances.find(item => item.channelType === 'aun' && item.channel.getAidState()?.aid === options.fromAid);
2947
+ if (!instance)
2948
+ throw new Error('Current Agent AUN channel is unavailable');
2949
+ return instance.channel.requestRemoteMenu(options);
2950
+ });
2951
+ });
2862
2952
  ipcServer.setGroupOperationExecutor((argv, sessionId, delegationToken, delegationCommandHash) => cmdHandler.handleGroupOperation(argv, sessionId, delegationToken, delegationCommandHash));
2863
2953
  ipcServer.setMsgOperationExecutor((argv, sessionId, delegationToken, delegationCommandHash) => cmdHandler.handleMsgOperation(argv, sessionId, delegationToken, delegationCommandHash));
2864
2954
  ipcServer.setFsOperationExecutor((argv, sessionId, delegationToken, delegationCommandHash) => cmdHandler.handleFsOperation(argv, sessionId, delegationToken, delegationCommandHash));
@@ -3005,7 +3095,14 @@ async function main() {
3005
3095
  error: decision.reason,
3006
3096
  };
3007
3097
  }
3008
- return { ok: true, aid: selfAid, currentSessionId: params.sessionId, managementRole };
3098
+ return {
3099
+ ok: true,
3100
+ aid: selfAid,
3101
+ currentSessionId: params.sessionId,
3102
+ managementRole,
3103
+ role: subject.role,
3104
+ actorId: delegation.grant.actorId,
3105
+ };
3009
3106
  };
3010
3107
  const resolveHandoffQueryScope = async (params, operation) => {
3011
3108
  if (params.sessionId) {
@@ -3027,6 +3124,22 @@ async function main() {
3027
3124
  return scope;
3028
3125
  return responseEngine.returnAuthorizedHandoffResult({ sessionId: params.sessionId, handoffId: params.handoffId, content: params.content }, scope.aid);
3029
3126
  });
3127
+ ipcServer.setHandoffRecoverReturnExecutor(async (params) => {
3128
+ if (!params.sessionId)
3129
+ return { ok: false, code: 'HANDOFF_CALL_SESSION_INVALID', error: 'current session is required' };
3130
+ const scope = await resolveManagedHandoffScope({ ...params, sessionId: params.sessionId }, 'ec.handoff.recover');
3131
+ if (!scope.ok)
3132
+ return scope;
3133
+ if (scope.role !== 'owner') {
3134
+ return { ok: false, code: 'ROLE_ACCESS_DENIED', error: 'handoff recovery requires the current Agent owner' };
3135
+ }
3136
+ return responseEngine.recoverAuthorizedHandoffResult({
3137
+ sessionId: params.sessionId,
3138
+ handoffId: params.handoffId,
3139
+ content: params.content,
3140
+ actorId: scope.actorId,
3141
+ }, scope.aid);
3142
+ });
3030
3143
  ipcServer.setHandoffStatusExecutor(async (params) => {
3031
3144
  const scope = await resolveHandoffQueryScope(params, 'ec.handoff.status');
3032
3145
  if (!scope.ok)
@@ -3177,6 +3290,7 @@ async function main() {
3177
3290
  thread: params.thread,
3178
3291
  targetChatType: params.scope === 'group' ? 'group' : 'private',
3179
3292
  explicitReturnPolicy: params.returnPolicy,
3293
+ handoffContext: params.handoffContext,
3180
3294
  originAuthorization: {
3181
3295
  actorId: delegation.grant.actorId,
3182
3296
  channelKey: delegation.grant.channel,
@@ -3803,6 +3917,7 @@ async function main() {
3803
3917
  ok: true,
3804
3918
  aid: agent.aid,
3805
3919
  status: agent.status,
3920
+ lifecycle: agent.lifecycle ?? null,
3806
3921
  identity: agent.identity ?? { name: null, description: null },
3807
3922
  config: {
3808
3923
  baseagent: agent.config?.baseagent ?? null,
@@ -3879,6 +3994,7 @@ async function main() {
3879
3994
  handoff: value('--handoff'),
3880
3995
  name: value('--name'),
3881
3996
  description: value('--description'),
3997
+ type: value('--type'),
3882
3998
  tags,
3883
3999
  force: argv.includes('--force'),
3884
4000
  });
@@ -3936,6 +4052,7 @@ async function main() {
3936
4052
  owners: plan.owners,
3937
4053
  name: plan.name,
3938
4054
  description: plan.description,
4055
+ type: plan.type,
3939
4056
  tags: plan.tags,
3940
4057
  force: argv.includes('--force'),
3941
4058
  };