evolcore 0.0.24 → 0.0.25

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 (197) hide show
  1. package/CHANGELOG.md +578 -561
  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 +29 -29
  8. package/bin/install-codex-managed-hooks.mjs +555 -555
  9. package/dist/agents/claude-runner.js +4 -1
  10. package/dist/agents/codex-runner.js +7 -4
  11. package/dist/agents/ecagent-runner.js +14 -13
  12. package/dist/agents/gemini-runner.js +22 -2
  13. package/dist/aun/aid/agentmd.js +56 -4
  14. package/dist/aun/aid/index.js +1 -0
  15. package/dist/aun/aid/managed-operation.js +53 -1
  16. package/dist/aun/storage/client.js +4 -1
  17. package/dist/aun/storage/index.js +1 -1
  18. package/dist/cli/agent-command.js +186 -87
  19. package/dist/cli/agent-create.js +42 -4
  20. package/dist/cli/agent.js +103 -1
  21. package/dist/cli/aun-commands.js +360 -294
  22. package/dist/cli/bench.js +20 -20
  23. package/dist/cli/config.js +20 -20
  24. package/dist/cli/contact.js +10 -10
  25. package/dist/cli/ctl-command.js +22 -22
  26. package/dist/cli/daemon-commands.js +15 -15
  27. package/dist/cli/fs-command.js +67 -67
  28. package/dist/cli/handoff-command.js +7 -7
  29. package/dist/cli/index.js +77 -77
  30. package/dist/cli/link-rules.js +21 -21
  31. package/dist/cli/menu-client.js +11 -11
  32. package/dist/cli/net-check.js +22 -22
  33. package/dist/cli/queue-command.js +29 -29
  34. package/dist/cli/response.js +30 -30
  35. package/dist/cli/restart-monitor.js +30 -30
  36. package/dist/cli/stats.js +51 -51
  37. package/dist/cli/trigger-command.js +56 -56
  38. package/dist/config/builtin-roles.js +1 -0
  39. package/dist/config/config-manager.js +15 -0
  40. package/dist/config/config-operation-service.js +20 -3
  41. package/dist/config/resolved-config-op.js +39 -1
  42. package/dist/core/auth/operation-authorizer.js +62 -4
  43. package/dist/core/auth/operation-catalog.js +16 -0
  44. package/dist/core/command/agent-control.js +54 -25
  45. package/dist/core/command/cli-intent-parser.js +18 -0
  46. package/dist/core/command/command-handler.js +91 -16
  47. package/dist/core/command/menu-catalog.js +1 -0
  48. package/dist/core/command/menu-handler.js +41 -5
  49. package/dist/core/command/menu-protocol.js +1 -0
  50. package/dist/core/command/slash-handler.js +9 -9
  51. package/dist/core/event-catalog.js +5 -0
  52. package/dist/core/evolagent.js +3 -3
  53. package/dist/core/message/response-engine.js +12 -6
  54. package/dist/core/permission/tool-policy.js +45 -6
  55. package/dist/index.js +209 -162
  56. package/dist/stats/db.js +159 -159
  57. package/dist/stats/eck-vars.js +9 -9
  58. package/dist/stats/query.js +139 -139
  59. package/dist/stats/role-budget.js +6 -6
  60. package/dist/stats/writer.js +51 -51
  61. package/dist/utils/avatar-upload.js +282 -23
  62. package/dist/utils/error-dict.json +160 -160
  63. package/dist/utils/macos-autostart.js +22 -22
  64. package/dist/utils/model-prices.jsonl +23 -23
  65. package/dist/utils/welcome.js +22 -22
  66. package/ecagent/LICENSE +21 -21
  67. package/ecagent/NOTICE +11 -11
  68. package/ecagent/README.md +61 -61
  69. package/ecagent/dist/harness/compaction/branch-summarization.js +30 -30
  70. package/ecagent/dist/harness/compaction/compaction.js +83 -83
  71. package/ecagent/dist/harness/messages.js +7 -7
  72. package/kits/docs/GUIDE.md +20 -20
  73. package/kits/docs/INDEX.md +66 -66
  74. package/kits/docs/aun/CHEATSHEET.md +19 -19
  75. package/kits/docs/aun/SYNC_PROTOCOL.md +15 -15
  76. package/kits/docs/channels/aun.md +56 -56
  77. package/kits/docs/channels/feishu.md +56 -56
  78. package/kits/docs/context-assembly.md +365 -364
  79. package/kits/docs/eck_templates/GUIDE.template.md +22 -22
  80. package/kits/docs/eck_templates/INDEX.template.md +28 -28
  81. package/kits/docs/eck_templates/path-registry.template.md +33 -33
  82. package/kits/docs/eck_templates/runtime.template.md +19 -19
  83. package/kits/docs/evolcore/INDEX.md +71 -69
  84. package/kits/docs/evolcore/agent-create.md +170 -156
  85. package/kits/docs/evolcore/agent.md +83 -73
  86. package/kits/docs/evolcore/aid.md +64 -62
  87. package/kits/docs/evolcore/codex-windows-sandbox.md +146 -0
  88. package/kits/docs/evolcore/config.md +195 -195
  89. package/kits/docs/evolcore/contact.md +68 -68
  90. package/kits/docs/evolcore/ctl.md +46 -46
  91. package/kits/docs/evolcore/event.md +216 -216
  92. package/kits/docs/evolcore/fs-architecture.md +1215 -1215
  93. package/kits/docs/evolcore/fs.md +110 -110
  94. package/kits/docs/evolcore/group-collaboration.md +243 -243
  95. package/kits/docs/evolcore/group-fs.md +17 -17
  96. package/kits/docs/evolcore/group-rules.md +251 -251
  97. package/kits/docs/evolcore/group.md +152 -152
  98. package/kits/docs/evolcore/menu-client.md +34 -34
  99. package/kits/docs/evolcore/model.md +50 -50
  100. package/kits/docs/evolcore/msg.md +165 -165
  101. package/kits/docs/evolcore/response.md +75 -75
  102. package/kits/docs/evolcore/rpc.md +37 -37
  103. package/kits/docs/evolcore/self-summary.md +29 -29
  104. package/kits/docs/evolcore/stats.md +83 -83
  105. package/kits/docs/evolcore/storage.md +50 -50
  106. package/kits/docs/evolcore/trigger.md +575 -575
  107. package/kits/docs/identity/AID_PROFILE_SPEC.md +26 -26
  108. package/kits/docs/identity/PATH_OPS.md +16 -16
  109. package/kits/docs/identity/ROLE_DETAIL.md +25 -25
  110. package/kits/docs/identity/identity-tools.md +26 -26
  111. package/kits/docs/path-registry.md +62 -62
  112. package/kits/docs/prompt-loading-architecture.md +270 -269
  113. package/kits/docs/venues/aun-group.md +45 -45
  114. package/kits/docs/venues/aun-private.md +10 -10
  115. package/kits/docs/venues/client-desktop.md +10 -10
  116. package/kits/docs/venues/client-mobile.md +10 -10
  117. package/kits/docs/venues/feishu-group.md +13 -13
  118. package/kits/docs/venues/feishu-private.md +9 -9
  119. package/kits/docs/venues/group.md +25 -25
  120. package/kits/docs/venues/private.md +10 -10
  121. package/kits/eck_manifest.auxiliary.json +43 -43
  122. package/kits/eck_manifest.json +195 -183
  123. package/kits/eck_message_manifest.json +74 -74
  124. package/kits/migrations/README-role-config-v4.md +32 -32
  125. package/kits/migrations/migrate-contact-book-v2.mjs +748 -748
  126. package/kits/migrations/migrate-role-config-v4.mjs +614 -614
  127. package/kits/migrations/migrate-role-config-v5.mjs +365 -365
  128. package/kits/migrations/rename-config-file.mjs +99 -99
  129. package/kits/rules/01-overview.md +162 -162
  130. package/kits/rules/02-navigation.md +96 -96
  131. package/kits/rules/03-identity.md +39 -37
  132. package/kits/rules/04-relation.md +75 -75
  133. package/kits/rules/05-venue.md +60 -60
  134. package/kits/rules/06-channel.md +63 -63
  135. package/kits/schemas/_meta.json +53 -53
  136. package/kits/schemas/agent-config.schema.1.json +177 -177
  137. package/kits/schemas/agent-config.schema.10.json +395 -395
  138. package/kits/schemas/agent-config.schema.11.json +421 -421
  139. package/kits/schemas/agent-config.schema.12.json +427 -427
  140. package/kits/schemas/agent-config.schema.2.json +239 -239
  141. package/kits/schemas/agent-config.schema.3.json +119 -119
  142. package/kits/schemas/agent-config.schema.4.json +208 -208
  143. package/kits/schemas/agent-config.schema.5.json +326 -326
  144. package/kits/schemas/agent-config.schema.6.json +322 -322
  145. package/kits/schemas/agent-config.schema.7.json +303 -303
  146. package/kits/schemas/agent-config.schema.8.json +304 -304
  147. package/kits/schemas/agent-config.schema.9.json +364 -364
  148. package/kits/schemas/contact-book.schema.1.json +36 -36
  149. package/kits/schemas/contact-book.schema.2.json +43 -43
  150. package/kits/schemas/contact-book.schema.3.json +67 -67
  151. package/kits/schemas/daemon.schema.1.json +91 -91
  152. package/kits/schemas/daemon.schema.2.json +101 -101
  153. package/kits/schemas/daemon.schema.3.json +123 -123
  154. package/kits/schemas/daemon.schema.4.json +136 -136
  155. package/kits/schemas/daemon.schema.5.json +135 -135
  156. package/kits/schemas/daemon.schema.6.json +131 -131
  157. package/kits/schemas/defaults.schema.1.json +81 -81
  158. package/kits/schemas/defaults.schema.2.json +85 -85
  159. package/kits/schemas/defaults.schema.3.json +73 -73
  160. package/kits/schemas/defaults.schema.4.json +107 -107
  161. package/kits/schemas/defaults.schema.5.json +119 -119
  162. package/kits/schemas/menu-exec-schema-commands.md +208 -208
  163. package/kits/schemas/migrations/README.md +30 -30
  164. package/kits/schemas/relation-config.schema.1.json +158 -158
  165. package/kits/schemas/relation-config.schema.2.json +73 -73
  166. package/kits/schemas/relation-config.schema.3.json +50 -50
  167. package/kits/schemas/relation-config.schema.4.json +47 -47
  168. package/kits/schemas/relation-config.schema.5.json +47 -47
  169. package/kits/schemas/relation-config.schema.6.json +46 -46
  170. package/kits/schemas/relation-config.schema.7.json +59 -59
  171. package/kits/schemas/relation-config.schema.8.json +103 -103
  172. package/kits/schemas/role-config.schema.1.json +201 -201
  173. package/kits/schemas/role-registry.schema.1.json +35 -35
  174. package/kits/schemas/single-session.schema.1.json +31 -31
  175. package/kits/schemas/single-session.schema.2.json +57 -57
  176. package/kits/schemas/single-session.schema.3.json +32 -32
  177. package/kits/templates/bootstrap-welcome.md +17 -17
  178. package/kits/templates/message-fragments/handoff-context-to-target.md +9 -9
  179. package/kits/templates/message-fragments/handoff-request-to-target.md +19 -19
  180. package/kits/templates/message-fragments/handoff-response-to-origin.md +9 -9
  181. package/kits/templates/message-fragments/inject-default.md +2 -2
  182. package/kits/templates/message-fragments/item.md +2 -2
  183. package/kits/templates/roles/admin.json +96 -84
  184. package/kits/templates/roles/member.json +97 -96
  185. package/kits/templates/roles/owner.json +9 -9
  186. package/kits/templates/roles/visitor.json +86 -85
  187. package/kits/templates/system-fragments/baseagent.md +14 -14
  188. package/kits/templates/system-fragments/bootstrap.md +38 -29
  189. package/kits/templates/system-fragments/channel.md +64 -64
  190. package/kits/templates/system-fragments/commands.md +28 -28
  191. package/kits/templates/system-fragments/identity.md +13 -13
  192. package/kits/templates/system-fragments/relation.md +19 -19
  193. package/kits/templates/system-fragments/session.md +59 -59
  194. package/kits/templates/system-fragments/venue.md +31 -31
  195. package/package.json +98 -98
  196. package/skills/eclink/SKILL.md +0 -84
  197. package/skills/eclink/agents/openai.yaml +0 -4
@@ -2998,6 +2998,12 @@ export class ResponseEngine {
2998
2998
  bootstrapAgentMdPath: agentMdPath(selfAid),
2999
2999
  };
3000
3000
  }
3001
+ if (selfAid && peerRole === 'owner' && effectivePermissionMode !== 'readonly') {
3002
+ modelOverride = {
3003
+ ...(modelOverride || {}),
3004
+ personalMdPath: path.join(resolveRoot(), 'agents', selfAid, 'personal', 'personal.md'),
3005
+ };
3006
+ }
3001
3007
  if (normalizedBaseagent.canonical === 'claude') {
3002
3008
  modelOverride = {
3003
3009
  ...(modelOverride || {}),
@@ -4561,12 +4567,12 @@ export class ResponseEngine {
4561
4567
  return undefined;
4562
4568
  const shouldClose = !writerDb;
4563
4569
  try {
4564
- const row = db.prepare(`SELECT model, input_tokens, cache_creation_tokens, cache_read_tokens,
4565
- context_tokens, max_tokens, auto_compact_tokens
4566
- FROM model_calls
4567
- WHERE session_id = ?
4568
- AND agent_session_id = ?
4569
- ORDER BY ts DESC, call_index DESC
4570
+ const row = db.prepare(`SELECT model, input_tokens, cache_creation_tokens, cache_read_tokens,
4571
+ context_tokens, max_tokens, auto_compact_tokens
4572
+ FROM model_calls
4573
+ WHERE session_id = ?
4574
+ AND agent_session_id = ?
4575
+ ORDER BY ts DESC, call_index DESC
4570
4576
  LIMIT 1`).get(sessionId, agentSessionId);
4571
4577
  if (!row)
4572
4578
  return undefined;
@@ -1,5 +1,6 @@
1
1
  import fs from 'fs';
2
2
  import path from 'path';
3
+ import os from 'os';
3
4
  import { randomUUID } from 'crypto';
4
5
  import { logger } from '../../utils/logger.js';
5
6
  import { resolveRoot } from '../../paths.js';
@@ -41,6 +42,14 @@ export function expandManagedTempPath(value, options = {}) {
41
42
  }
42
43
  const WRITE_INTENT_RE = /(?:^|[\s;&|])(?:mkdir|mktemp|touch|tee|cp|mv|install|chmod|chown|ln|rm)(?:[\s;&|]|$)|(?:^|[\s])(?:\d>>?|>>?)\s*/i;
43
44
  const TEMP_TOKEN_RE = /(?:\$TMPDIR|\$\{TMPDIR\}|\/tmp(?:\/|$)|\/var\/tmp(?:\/|$)|\/dev\/shm(?:\/|$))[^\s'";&|<>)]*/g;
45
+ function windowsTempRoots(options) {
46
+ if (process.platform !== 'win32')
47
+ return [];
48
+ return [os.tmpdir(), options.env?.TEMP, options.env?.TMP, process.env.TEMP, process.env.TMP]
49
+ .filter((value) => typeof value === 'string' && path.isAbsolute(value))
50
+ .map(value => resolveProtectedCandidate(value))
51
+ .filter((value, index, values) => values.indexOf(value) === index);
52
+ }
44
53
  /**
45
54
  * Inspect shell write targets that look like temporary paths. Ordinary
46
55
  * project writes are left to the existing workspace/H-class policy; any
@@ -52,19 +61,32 @@ export function checkManagedTempShellCommand(command, options = {}) {
52
61
  const root = resolveManagedTempDir(options.env);
53
62
  const normalized = substituteTmpDir(command, root);
54
63
  const candidates = [...normalized.matchAll(TEMP_TOKEN_RE)].map(match => match[0].replace(/[.,:]+$/, ''));
55
- if (candidates.length === 0)
64
+ // Windows has no portable `/tmp` spelling. Treat absolute paths below the
65
+ // host temporary roots as temporary operands too, so a sibling session
66
+ // directory cannot bypass the managed TMPDIR check.
67
+ if (process.platform === 'win32') {
68
+ const roots = windowsTempRoots(options);
69
+ const windowsCandidates = normalized.match(/[A-Za-z]:[\\/][^\s'";&|<>)]*/g) ?? [];
70
+ for (const candidate of windowsCandidates) {
71
+ const canonical = resolveProtectedCandidate(candidate.replace(/[.,:]+$/, ''), options.cwd);
72
+ if (roots.some(root => isSameOrDescendant(canonical, root)))
73
+ candidates.push(candidate.replace(/[.,:]+$/, ''));
74
+ }
75
+ }
76
+ const uniqueCandidates = [...new Set(candidates)];
77
+ if (uniqueCandidates.length === 0)
56
78
  return { kind: 'none' };
57
79
  if (!root) {
58
- return { kind: 'deny', paths: candidates, reason: 'TMPDIR is unset or not an absolute managed path' };
80
+ return { kind: 'deny', paths: uniqueCandidates, reason: 'TMPDIR is unset or not an absolute managed path' };
59
81
  }
60
- const outside = candidates.filter(candidate => !isManagedTempPath(candidate, {
82
+ const outside = uniqueCandidates.filter(candidate => !isManagedTempPath(candidate, {
61
83
  ...options,
62
84
  env: { ...(options.env ?? process.env), TMPDIR: root },
63
85
  }));
64
86
  if (outside.length > 0) {
65
87
  return { kind: 'deny', paths: outside, reason: 'temporary write target is outside the session-managed TMPDIR' };
66
88
  }
67
- return { kind: 'allow', paths: candidates };
89
+ return { kind: 'allow', paths: uniqueCandidates };
68
90
  }
69
91
  /**
70
92
  * Replace only already-validated temporary path operands before a second
@@ -2237,16 +2259,33 @@ export function evaluateToolPreflight(toolName, input, context) {
2237
2259
  && managedAidArgv[2] === 'agentmd'
2238
2260
  && managedAidArgv[3] === 'put'
2239
2261
  && ownAid;
2262
+ const avatarFileIndex = managedAidArgv?.indexOf('--file') ?? -1;
2263
+ const avatarUrlIndex = managedAidArgv?.indexOf('--url') ?? -1;
2264
+ const avatarSourceIndex = avatarFileIndex >= 0 ? avatarFileIndex : avatarUrlIndex;
2265
+ const isSelfAvatarUpload = !!managedAidArgv
2266
+ && (avatarSourceIndex === 4)
2267
+ && managedAidArgv.length === 6
2268
+ && managedAidArgv[0] === 'ec'
2269
+ && managedAidArgv[1] === 'aid'
2270
+ && managedAidArgv[2] === 'avatar'
2271
+ && !!context.selfAid
2272
+ && managedAidArgv[3]?.replace(/^@/, '') === context.selfAid.replace(/^@/, '')
2273
+ && !!managedAidArgv[5]
2274
+ && !managedAidArgv[5].startsWith('-');
2240
2275
  const canPublishSelfAgentMd = isSelfAgentMdPublish
2241
2276
  && ['request', 'bypass'].includes(context.permissionMode ?? '');
2242
- const isAllowedManagedAidCommand = isManagedAidPublicRead || isSelfManagedAidShow || canPublishSelfAgentMd;
2277
+ const canUploadSelfAvatar = isSelfAvatarUpload
2278
+ && ['request', 'bypass'].includes(context.permissionMode ?? '');
2279
+ const isAllowedManagedAidCommand = isManagedAidPublicRead || isSelfManagedAidShow || canPublishSelfAgentMd || canUploadSelfAvatar;
2243
2280
  if (context.sessionId && /^\s*ec\s+aid(?:\s|$)/i.test(policyCommand) && !isAllowedManagedAidCommand) {
2244
2281
  return {
2245
2282
  behavior: 'deny',
2246
2283
  input,
2247
2284
  message: isSelfAgentMdPublish && (context.permissionMode === 'readonly' || context.permissionMode === 'auto')
2248
2285
  ? `🔒 ${context.permissionMode} 模式禁止发布 agent.md;请切换到 request 或 bypass 后再执行 \`ec aid agentmd put <当前 self AID>\`。`
2249
- : '🔒 EvolCore 托管会话允许 `ec aid show <当前 self AID>`、`ec aid lookup <任意合法 AID>` 或 `ec aid agentmd get <任意合法 AID>`;`ec aid agentmd put` 还要求针对当前 self AID 且处于 request 或 bypass。可选后缀仅限 `--format json`。禁止列举本地身份、切换身份或对其它 AID 执行 show/put。',
2286
+ : isSelfAvatarUpload && (context.permissionMode === 'readonly' || context.permissionMode === 'auto')
2287
+ ? `🔒 ${context.permissionMode} 模式禁止上传头像;请切换到 request 或 bypass 后再执行 \`ec aid avatar <当前 self AID> (--file <path> 或 --url <https-url>)\`。`
2288
+ : '🔒 EvolCore 托管会话允许 `ec aid show <当前 self AID>`、`ec aid lookup <任意合法 AID>` 或 `ec aid agentmd get <任意合法 AID>`;`ec aid agentmd put` 及 `ec aid avatar` 还要求针对当前 self AID 且处于 request 或 bypass。可选后缀仅限 `--format json`。禁止列举本地身份、切换身份或对其它 AID 执行 show/put/avatar。',
2250
2289
  policyCode: 'managed_ec_aid_scope_forbidden',
2251
2290
  };
2252
2291
  }
package/dist/index.js CHANGED
@@ -333,153 +333,153 @@ function seedUpgradeCheckTrigger(manager, owner) {
333
333
  // take the watchdog down with it.
334
334
  const monitorEntry = platform.resolveCliEntry(packageRoot);
335
335
  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
- });
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
+ });
483
483
  `);
484
484
  // Remove the pre-Node script left by older installations so it cannot be
485
485
  // selected by a stale trigger definition or run manually by accident.
@@ -3663,7 +3663,8 @@ async function main() {
3663
3663
  });
3664
3664
  if (params.targetAid
3665
3665
  && params.targetAid !== delegation.grant.selfAid
3666
- && subject.dataScope !== 'daemon') {
3666
+ && subject.dataScope !== 'daemon'
3667
+ && !(operation === 'agent.show' && isManagementRole(subject.role))) {
3667
3668
  return { ok: false, code: 'SCOPE_MISMATCH', error: 'only the current Agent can be targeted' };
3668
3669
  }
3669
3670
  const decision = authorizeOperation({
@@ -3710,18 +3711,21 @@ async function main() {
3710
3711
  const taskAid = delegation.grant.selfAid;
3711
3712
  const targetAid = sub === 'list'
3712
3713
  ? undefined
3713
- : sub === 'show'
3714
- ? (argv[2] && !argv[2].startsWith('--') ? argv[2] : taskAid)
3715
- : sub === 'reload'
3716
- ? (argv[2] && !argv[2].startsWith('--') ? argv[2] : undefined)
3717
- : (argv[2] && !argv[2].startsWith('--') ? argv[2] : undefined);
3714
+ : sub === 'persona'
3715
+ ? taskAid
3716
+ : sub === 'show'
3717
+ ? (argv[2] && !argv[2].startsWith('--') ? argv[2] : taskAid)
3718
+ : sub === 'reload'
3719
+ ? (argv[2] && !argv[2].startsWith('--') ? argv[2] : undefined)
3720
+ : (argv[2] && !argv[2].startsWith('--') ? argv[2] : undefined);
3718
3721
  const operation = sub === 'list' ? 'agent.list'
3719
3722
  : sub === 'show' ? 'agent.show'
3720
- : sub === 'new' ? 'agent.create'
3721
- : sub === 'enable' ? 'agent.enable'
3722
- : sub === 'disable' ? 'agent.disable'
3723
- : sub === 'reload' ? 'agent.reload'
3724
- : sub === 'delete' ? 'agent.delete' : undefined;
3723
+ : sub === 'persona' ? 'agent.persona'
3724
+ : sub === 'new' ? 'agent.create'
3725
+ : sub === 'enable' ? 'agent.enable'
3726
+ : sub === 'disable' ? 'agent.disable'
3727
+ : sub === 'reload' ? 'agent.reload'
3728
+ : sub === 'delete' ? 'agent.delete' : undefined;
3725
3729
  if (!operation)
3726
3730
  return fail('INVALID_ARGS', `unsupported managed agent command: ${sub}`);
3727
3731
  if (sub === 'new' && !argv.includes('--non-interactive')) {
@@ -3753,7 +3757,7 @@ async function main() {
3753
3757
  subject,
3754
3758
  intent: {
3755
3759
  operation,
3756
- scope: operation === 'agent.show'
3760
+ scope: operation === 'agent.show' || operation === 'agent.persona'
3757
3761
  ? 'agent'
3758
3762
  : operation === 'agent.reload' && targetAid
3759
3763
  ? 'agent'
@@ -3809,16 +3813,50 @@ async function main() {
3809
3813
  },
3810
3814
  connection: agent.connection ?? null,
3811
3815
  sessions: agent.sessions ?? { active: 0, last_activity: null },
3812
- paths: { config: null, agent_md: null, project: null, data: null },
3813
- redacted: true,
3816
+ paths: subject.dataScope === 'daemon'
3817
+ ? (agent.paths ?? { config: null, agent_md: null, project: null, data: null })
3818
+ : { config: null, agent_md: null, project: null, data: null },
3819
+ ...(subject.dataScope === 'daemon' ? {} : { redacted: true }),
3814
3820
  ...(agent.createProgress ? { createProgress: agent.createProgress } : {}),
3815
3821
  } };
3816
3822
  }
3823
+ if (operation === 'agent.persona') {
3824
+ const rawPersonaArgs = argv.slice(2);
3825
+ const hasFormatSuffix = rawPersonaArgs.length >= 2
3826
+ && rawPersonaArgs.at(-2) === '--format'
3827
+ && rawPersonaArgs.at(-1) === 'json';
3828
+ const personaArgs = hasFormatSuffix ? rawPersonaArgs.slice(0, -2) : rawPersonaArgs;
3829
+ if (!hasFormatSuffix && rawPersonaArgs.includes('--format')) {
3830
+ return fail('INVALID_ARGS', 'persona 仅支持末尾的 --format json');
3831
+ }
3832
+ if (personaArgs.length === 2 && personaArgs[0] === '--file') {
3833
+ return fail('INVALID_ARGS', '托管 persona --file 必须由 CLI 读取后作为文本提交');
3834
+ }
3835
+ const wantsShow = personaArgs.length === 1 && personaArgs[0] === 'show';
3836
+ const text = personaArgs.length === 2 && personaArgs[0] === '--text'
3837
+ ? personaArgs[1]
3838
+ : undefined;
3839
+ if (!wantsShow && !(personaArgs.length === 2 && personaArgs[0] === '--text')) {
3840
+ return fail('INVALID_ARGS', '用法: ec agent persona show | --text <text>');
3841
+ }
3842
+ if (text === undefined && !wantsShow)
3843
+ return fail('INVALID_ARGS', 'persona --text 缺少参数');
3844
+ const { agentPersona } = await import('./cli/agent.js');
3845
+ const result = await agentPersona(taskAid, wantsShow ? { trustedLocal: true } : { text, trustedLocal: true });
3846
+ if (!result.ok)
3847
+ return fail(result.code ?? 'INTERNAL', result.error);
3848
+ return { ok: true, result: {
3849
+ ok: true,
3850
+ aid: taskAid,
3851
+ content: result.content,
3852
+ ...(result.saved ? { saved: true } : {}),
3853
+ } };
3854
+ }
3817
3855
  if (operation === 'agent.create') {
3818
3856
  const aid = argv[2] && !argv[2].startsWith('--') ? argv[2] : undefined;
3819
3857
  if (!aid)
3820
3858
  return fail('INVALID_ARGS', 'ec agent new requires an AID');
3821
- const { agentCreateNonInteractive, resolveAgentCreatePlan, validateAgentCreatePlan, parseAgentOwnerArgs } = await import('./cli/agent-create.js');
3859
+ const { agentCreateNonInteractive, resolveAgentCreatePlan, validateAgentCreatePlan, parseAgentOwnerArgs, parseAgentTags } = await import('./cli/agent-create.js');
3822
3860
  let ownerOptions;
3823
3861
  try {
3824
3862
  ownerOptions = parseAgentOwnerArgs(argv);
@@ -3826,6 +3864,13 @@ async function main() {
3826
3864
  catch (error) {
3827
3865
  return fail('INVALID_ARGS', error instanceof Error ? error.message : String(error));
3828
3866
  }
3867
+ let tags;
3868
+ try {
3869
+ tags = parseAgentTags(value('--tags'));
3870
+ }
3871
+ catch (error) {
3872
+ return fail('INVALID_ARGS', error instanceof Error ? error.message : String(error));
3873
+ }
3829
3874
  const plan = resolveAgentCreatePlan({
3830
3875
  aid,
3831
3876
  project: value('--project'),
@@ -3834,6 +3879,7 @@ async function main() {
3834
3879
  handoff: value('--handoff'),
3835
3880
  name: value('--name'),
3836
3881
  description: value('--description'),
3882
+ tags,
3837
3883
  force: argv.includes('--force'),
3838
3884
  });
3839
3885
  const validationError = validateAgentCreatePlan(plan);
@@ -3890,6 +3936,7 @@ async function main() {
3890
3936
  owners: plan.owners,
3891
3937
  name: plan.name,
3892
3938
  description: plan.description,
3939
+ tags: plan.tags,
3893
3940
  force: argv.includes('--force'),
3894
3941
  };
3895
3942
  try {