evolcore 0.0.23 → 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 (245) hide show
  1. package/CHANGELOG.md +583 -516
  2. package/LICENSE +21 -21
  3. package/README.md +455 -453
  4. package/bin/codex-managed-acl.ps1 +160 -155
  5. package/bin/codex-managed-hook.mjs +209 -92
  6. package/bin/ec-safe-output.js +327 -245
  7. package/bin/ec.js +29 -29
  8. package/bin/install-codex-managed-hooks.mjs +555 -544
  9. package/dist/agents/baseagent-availability.js +4 -4
  10. package/dist/agents/baseagent.js +29 -1
  11. package/dist/agents/claude-runner.js +152 -14
  12. package/dist/agents/codex/app-server-client.js +18 -8
  13. package/dist/agents/codex/catalog.js +1 -1
  14. package/dist/agents/codex/diagnostics.js +215 -0
  15. package/dist/agents/codex-runner.js +445 -63
  16. package/dist/agents/ecagent-runner.js +31 -16
  17. package/dist/agents/gemini-runner.js +82 -4
  18. package/dist/agents/runner-types.js +1 -1
  19. package/dist/aun/aid/agentmd.js +56 -4
  20. package/dist/aun/aid/index.js +1 -0
  21. package/dist/aun/aid/managed-operation.js +80 -6
  22. package/dist/aun/aid/store.js +41 -0
  23. package/dist/aun/msg/managed-operation.js +6 -1
  24. package/dist/aun/outbox.js +56 -18
  25. package/dist/aun/rpc/client.js +4 -5
  26. package/dist/aun/storage/client.js +4 -1
  27. package/dist/aun/storage/index.js +1 -1
  28. package/dist/channels/aun.js +147 -76
  29. package/dist/channels/daemon.js +7 -1
  30. package/dist/channels/dingtalk.js +1 -0
  31. package/dist/channels/feishu.js +10 -4
  32. package/dist/channels/wecom.js +2 -1
  33. package/dist/cli/agent-command.js +214 -89
  34. package/dist/cli/agent-create.js +576 -0
  35. package/dist/cli/agent.js +129 -676
  36. package/dist/cli/aun-commands.js +419 -313
  37. package/dist/cli/bench.js +20 -20
  38. package/dist/cli/config.js +20 -20
  39. package/dist/cli/contact.js +10 -10
  40. package/dist/cli/ctl-command.js +22 -20
  41. package/dist/cli/daemon-commands.js +163 -32
  42. package/dist/cli/fs-command.js +67 -67
  43. package/dist/cli/handoff-command.js +7 -7
  44. package/dist/cli/index.js +79 -79
  45. package/dist/cli/link-rules.js +21 -21
  46. package/dist/cli/managed-command-guard.js +3 -0
  47. package/dist/cli/menu-client.js +11 -11
  48. package/dist/cli/net-check.js +22 -22
  49. package/dist/cli/queue-command.js +29 -29
  50. package/dist/cli/response.js +30 -30
  51. package/dist/cli/restart-monitor.js +462 -38
  52. package/dist/cli/stats.js +161 -51
  53. package/dist/cli/trigger-command.js +56 -56
  54. package/dist/config/builtin-roles.js +3 -0
  55. package/dist/config/config-manager.js +15 -0
  56. package/dist/config/config-operation-service.js +20 -3
  57. package/dist/config/resolved-config-op.js +39 -1
  58. package/dist/core/auth/auth-gateway.js +3 -2
  59. package/dist/core/auth/authorization-audit.js +72 -19
  60. package/dist/core/auth/operation-authorizer.js +125 -7
  61. package/dist/core/auth/operation-catalog.js +26 -2
  62. package/dist/core/auth/remote-access-store.js +3 -2
  63. package/dist/core/baseagent-loader.js +4 -4
  64. package/dist/core/bootstrap-messages.js +39 -18
  65. package/dist/core/bootstrap-service.js +32 -26
  66. package/dist/core/capability/capability-manager.js +8 -4
  67. package/dist/core/capability/providers/codex-capability-provider.js +1 -1
  68. package/dist/core/command/agent-control.js +60 -26
  69. package/dist/core/command/cli-intent-parser.js +18 -0
  70. package/dist/core/command/command-handler.js +173 -36
  71. package/dist/core/command/menu-catalog.js +4 -1
  72. package/dist/core/command/menu-handler.js +315 -75
  73. package/dist/core/command/menu-protocol.js +7 -2
  74. package/dist/core/command/slash-gate.js +1 -3
  75. package/dist/core/command/slash-handler.js +166 -57
  76. package/dist/core/data-migration.js +5 -15
  77. package/dist/core/event-catalog.js +8 -0
  78. package/dist/core/evolagent.js +3 -3
  79. package/dist/core/handoff/runtime.js +3 -1
  80. package/dist/core/handoff/store.js +7 -1
  81. package/dist/core/message/im-renderer.js +13 -6
  82. package/dist/core/message/message-bridge.js +5 -36
  83. package/dist/core/message/message-utils.js +6 -3
  84. package/dist/core/message/response-engine.js +85 -27
  85. package/dist/core/model/catalog-service.js +3 -1
  86. package/dist/core/model/model-catalog.js +10 -0
  87. package/dist/core/model/model-permission.js +4 -1
  88. package/dist/core/permission/approval-gateway.js +169 -33
  89. package/dist/core/permission/tool-policy.js +56 -10
  90. package/dist/core/session/adapters/codex-session-file-adapter.js +71 -3
  91. package/dist/core/session/session-manager.js +49 -13
  92. package/dist/core/session/session-renew.js +1 -0
  93. package/dist/index.js +327 -134
  94. package/dist/ipc.js +68 -21
  95. package/dist/paths.js +23 -1
  96. package/dist/stats/billing.js +18 -4
  97. package/dist/stats/db.js +159 -159
  98. package/dist/stats/eck-vars.js +9 -9
  99. package/dist/stats/query.js +139 -139
  100. package/dist/stats/role-budget.js +6 -6
  101. package/dist/stats/writer.js +51 -51
  102. package/dist/utils/atomic-write.js +26 -2
  103. package/dist/utils/avatar-upload.js +282 -23
  104. package/dist/utils/cross-platform.js +436 -1
  105. package/dist/utils/error-dict.json +160 -160
  106. package/dist/utils/error-utils.js +11 -4
  107. package/dist/utils/macos-autostart.js +22 -22
  108. package/dist/utils/model-prices.jsonl +23 -20
  109. package/dist/utils/npm-ops.js +24 -1
  110. package/dist/utils/welcome.js +22 -22
  111. package/dist/utils/windows-autostart.js +1 -1
  112. package/ecagent/LICENSE +21 -21
  113. package/ecagent/NOTICE +11 -11
  114. package/ecagent/README.md +61 -61
  115. package/ecagent/dist/harness/compaction/branch-summarization.js +30 -30
  116. package/ecagent/dist/harness/compaction/compaction.js +83 -83
  117. package/ecagent/dist/harness/messages.js +7 -7
  118. package/kits/docs/GUIDE.md +20 -20
  119. package/kits/docs/INDEX.md +66 -66
  120. package/kits/docs/aun/CHEATSHEET.md +19 -19
  121. package/kits/docs/aun/SYNC_PROTOCOL.md +15 -15
  122. package/kits/docs/channels/aun.md +56 -56
  123. package/kits/docs/channels/feishu.md +56 -56
  124. package/kits/docs/context-assembly.md +365 -364
  125. package/kits/docs/eck_templates/GUIDE.template.md +22 -22
  126. package/kits/docs/eck_templates/INDEX.template.md +28 -28
  127. package/kits/docs/eck_templates/path-registry.template.md +33 -33
  128. package/kits/docs/eck_templates/runtime.template.md +19 -19
  129. package/kits/docs/evolcore/INDEX.md +71 -69
  130. package/kits/docs/evolcore/agent-create.md +170 -156
  131. package/kits/docs/evolcore/agent.md +83 -73
  132. package/kits/docs/evolcore/aid.md +64 -62
  133. package/kits/docs/evolcore/codex-windows-sandbox.md +146 -0
  134. package/kits/docs/evolcore/config.md +195 -195
  135. package/kits/docs/evolcore/contact.md +68 -68
  136. package/kits/docs/evolcore/ctl.md +46 -46
  137. package/kits/docs/evolcore/event.md +216 -216
  138. package/kits/docs/evolcore/fs-architecture.md +1215 -1215
  139. package/kits/docs/evolcore/fs.md +110 -110
  140. package/kits/docs/evolcore/group-collaboration.md +243 -243
  141. package/kits/docs/evolcore/group-fs.md +17 -17
  142. package/kits/docs/evolcore/group-rules.md +251 -251
  143. package/kits/docs/evolcore/group.md +152 -152
  144. package/kits/docs/evolcore/menu-client.md +34 -34
  145. package/kits/docs/evolcore/model.md +50 -50
  146. package/kits/docs/evolcore/msg.md +165 -165
  147. package/kits/docs/evolcore/response.md +75 -75
  148. package/kits/docs/evolcore/rpc.md +37 -37
  149. package/kits/docs/evolcore/self-summary.md +29 -29
  150. package/kits/docs/evolcore/stats.md +83 -83
  151. package/kits/docs/evolcore/storage.md +50 -50
  152. package/kits/docs/evolcore/trigger.md +575 -575
  153. package/kits/docs/identity/AID_PROFILE_SPEC.md +26 -26
  154. package/kits/docs/identity/PATH_OPS.md +16 -16
  155. package/kits/docs/identity/ROLE_DETAIL.md +25 -25
  156. package/kits/docs/identity/identity-tools.md +26 -26
  157. package/kits/docs/path-registry.md +62 -62
  158. package/kits/docs/prompt-loading-architecture.md +270 -269
  159. package/kits/docs/venues/aun-group.md +45 -45
  160. package/kits/docs/venues/aun-private.md +10 -10
  161. package/kits/docs/venues/client-desktop.md +10 -10
  162. package/kits/docs/venues/client-mobile.md +10 -10
  163. package/kits/docs/venues/feishu-group.md +13 -13
  164. package/kits/docs/venues/feishu-private.md +9 -9
  165. package/kits/docs/venues/group.md +25 -25
  166. package/kits/docs/venues/private.md +10 -10
  167. package/kits/eck_manifest.auxiliary.json +43 -43
  168. package/kits/eck_manifest.json +195 -183
  169. package/kits/eck_message_manifest.json +74 -74
  170. package/kits/migrations/README-role-config-v4.md +32 -32
  171. package/kits/migrations/migrate-contact-book-v2.mjs +748 -748
  172. package/kits/migrations/migrate-role-config-v4.mjs +614 -614
  173. package/kits/migrations/migrate-role-config-v5.mjs +365 -365
  174. package/kits/migrations/rename-config-file.mjs +99 -99
  175. package/kits/rules/01-overview.md +162 -162
  176. package/kits/rules/02-navigation.md +96 -96
  177. package/kits/rules/03-identity.md +39 -37
  178. package/kits/rules/04-relation.md +75 -75
  179. package/kits/rules/05-venue.md +60 -60
  180. package/kits/rules/06-channel.md +63 -63
  181. package/kits/schemas/_meta.json +53 -53
  182. package/kits/schemas/agent-config.schema.1.json +177 -177
  183. package/kits/schemas/agent-config.schema.10.json +395 -395
  184. package/kits/schemas/agent-config.schema.11.json +421 -421
  185. package/kits/schemas/agent-config.schema.12.json +427 -427
  186. package/kits/schemas/agent-config.schema.2.json +239 -239
  187. package/kits/schemas/agent-config.schema.3.json +119 -119
  188. package/kits/schemas/agent-config.schema.4.json +208 -208
  189. package/kits/schemas/agent-config.schema.5.json +326 -326
  190. package/kits/schemas/agent-config.schema.6.json +322 -322
  191. package/kits/schemas/agent-config.schema.7.json +303 -303
  192. package/kits/schemas/agent-config.schema.8.json +304 -304
  193. package/kits/schemas/agent-config.schema.9.json +364 -364
  194. package/kits/schemas/contact-book.schema.1.json +36 -36
  195. package/kits/schemas/contact-book.schema.2.json +43 -43
  196. package/kits/schemas/contact-book.schema.3.json +67 -67
  197. package/kits/schemas/daemon.schema.1.json +91 -91
  198. package/kits/schemas/daemon.schema.2.json +101 -101
  199. package/kits/schemas/daemon.schema.3.json +123 -123
  200. package/kits/schemas/daemon.schema.4.json +136 -136
  201. package/kits/schemas/daemon.schema.5.json +135 -135
  202. package/kits/schemas/daemon.schema.6.json +131 -131
  203. package/kits/schemas/defaults.schema.1.json +81 -81
  204. package/kits/schemas/defaults.schema.2.json +85 -85
  205. package/kits/schemas/defaults.schema.3.json +73 -73
  206. package/kits/schemas/defaults.schema.4.json +107 -107
  207. package/kits/schemas/defaults.schema.5.json +119 -119
  208. package/kits/schemas/menu-exec-schema-commands.md +208 -208
  209. package/kits/schemas/migrations/README.md +30 -30
  210. package/kits/schemas/relation-config.schema.1.json +158 -158
  211. package/kits/schemas/relation-config.schema.2.json +73 -73
  212. package/kits/schemas/relation-config.schema.3.json +50 -50
  213. package/kits/schemas/relation-config.schema.4.json +47 -47
  214. package/kits/schemas/relation-config.schema.5.json +47 -47
  215. package/kits/schemas/relation-config.schema.6.json +46 -46
  216. package/kits/schemas/relation-config.schema.7.json +59 -59
  217. package/kits/schemas/relation-config.schema.8.json +103 -103
  218. package/kits/schemas/role-config.schema.1.json +201 -201
  219. package/kits/schemas/role-registry.schema.1.json +35 -35
  220. package/kits/schemas/single-session.schema.1.json +31 -31
  221. package/kits/schemas/single-session.schema.2.json +57 -57
  222. package/kits/schemas/single-session.schema.3.json +32 -32
  223. package/kits/templates/bootstrap-welcome.md +17 -17
  224. package/kits/templates/message-fragments/handoff-context-to-target.md +9 -9
  225. package/kits/templates/message-fragments/handoff-request-to-target.md +19 -19
  226. package/kits/templates/message-fragments/handoff-response-to-origin.md +9 -9
  227. package/kits/templates/message-fragments/inject-default.md +2 -2
  228. package/kits/templates/message-fragments/item.md +2 -2
  229. package/kits/templates/roles/admin.json +96 -82
  230. package/kits/templates/roles/member.json +97 -96
  231. package/kits/templates/roles/owner.json +9 -9
  232. package/kits/templates/roles/visitor.json +86 -85
  233. package/kits/templates/system-fragments/baseagent.md +14 -14
  234. package/kits/templates/system-fragments/bootstrap.md +38 -29
  235. package/kits/templates/system-fragments/channel.md +64 -64
  236. package/kits/templates/system-fragments/commands.md +28 -28
  237. package/kits/templates/system-fragments/identity.md +13 -13
  238. package/kits/templates/system-fragments/relation.md +19 -19
  239. package/kits/templates/system-fragments/session.md +59 -59
  240. package/kits/templates/system-fragments/venue.md +31 -31
  241. package/package.json +98 -94
  242. package/AGENTS.md +0 -62
  243. package/dist/utils/codex-cli.js +0 -43
  244. package/skills/eclink/SKILL.md +0 -84
  245. package/skills/eclink/agents/openai.yaml +0 -4
package/dist/cli/agent.js CHANGED
@@ -1,32 +1,19 @@
1
1
  import fs from 'fs';
2
2
  import path from 'path';
3
- import readline from 'readline';
4
- import { resolvePaths, agentMdPath as getAgentMdPathFromPaths, aunPath as defaultAunPath } from '../paths.js';
5
- import { loadDefaults, loadAllAgents, loadAgent, saveAgent, ensureAgentDirSkeleton, loadDaemonConfig } from '../config-store.js';
6
- import { ConfigTarget, write as cfgWrite, ensureFile as cfgEnsure, read as cfgRead, initConfigManager } from '../config/config-manager.js';
3
+ import { resolvePaths, agentMdPath as getAgentMdPathFromPaths, agentPersonalDir, aunPath as defaultAunPath } from '../paths.js';
4
+ import { loadDefaults, loadAllAgents, loadAgent, saveAgent, ensureAgentDirSkeleton } from '../config-store.js';
5
+ import { initConfigManager } from '../config/config-manager.js';
7
6
  import { resolveConfigCommand } from '../config/resolved-config-op.js';
8
7
  import { executeResolvedConfigCommand } from '../config/config-operation-service.js';
9
- import { ipcQuery } from '../ipc.js';
8
+ import { ipcQuery, IpcTransportError } from '../ipc.js';
10
9
  import { CONFIG_SCHEMA_VERSION } from '../types.js';
11
10
  import { ensureInitialRoleRegistry } from '../config/role-service.js';
12
- import { isValidChannelName } from '../core/channel-loader.js';
13
- import { commandExists } from '../utils/cross-platform.js';
14
- import { getCodexAppServerAvailability, isCodexAppServerAvailable } from '../agents/codex-runner.js';
15
- import { resolveEcagentConfig } from '../agents/baseagent.js';
16
11
  import { agentProjectRootFromDefaults, deriveAgentProjectPath } from '../utils/project-path.js';
12
+ import { isValidChannelName } from '../core/channel-loader.js';
17
13
  import { AGENT_DELEGATION_TOKEN_ENV } from '../core/auth/agent-delegation.js';
18
14
  import { readTaskRuntimeContextFromEnv } from './task-context.js';
19
- import { isValidAid } from '../aun/aid/validation.js';
20
- import { DEFAULT_AGENT_DESCRIPTION } from '../aun/aid/agentmd.js';
21
- function withStaticOwner(config, owner) {
22
- const value = owner?.trim();
23
- if (!value)
24
- return config;
25
- const owners = config.owners ?? [];
26
- if (owners.includes(value))
27
- return config;
28
- return { ...config, owners: [...owners, value] };
29
- }
15
+ import { isHClassPath, isLClassPath, isSameOrDescendant } from '../core/permission/protected-paths.js';
16
+ export { agentCreateInteractive, agentCreateNonInteractive, parseAgentOwnerArgs, resolveAgentCreatePlan, validateAgentCreatePlan, validateAgentOwnerOptions, } from './agent-create.js';
30
17
  /** Route agent lifecycle commands from a managed task through daemon IPC. */
31
18
  export async function runManagedAgentOperation(argv) {
32
19
  const sessionId = process.env.EVOLCORE_SESSION_ID;
@@ -53,174 +40,32 @@ export async function runManagedAgentOperation(argv) {
53
40
  return { ok: false, code: 'DAEMON_UNAVAILABLE', error: error instanceof Error ? error.message : String(error) };
54
41
  }
55
42
  }
56
- // ==================== Helpers ====================
57
- const BASEAGENT_CANDIDATES = ['claude', 'codex', 'gemini', 'ecagent'];
58
- const AUTO_BASEAGENT_CANDIDATES = ['claude', 'codex', 'gemini'];
59
- function isBaseagentAvailable(baseagent) {
60
- if (baseagent === 'codex')
61
- return isCodexAppServerAvailable();
62
- if (baseagent === 'ecagent')
63
- return true;
64
- return commandExists(baseagent);
65
- }
66
- function detectAvailableBaseagents() {
67
- return BASEAGENT_CANDIDATES.filter(isBaseagentAvailable);
68
- }
69
- function pickDefaultBaseagent(available) {
70
- return AUTO_BASEAGENT_CANDIDATES.find(candidate => available.includes(candidate)) ?? null;
71
- }
72
- function readProjectMetadata(project) {
73
- try {
74
- const packageJsonPath = path.join(project, 'package.json');
75
- if (!fs.existsSync(packageJsonPath))
76
- return {};
77
- const parsed = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
78
- return {
79
- name: typeof parsed.name === 'string' ? parsed.name.trim() : undefined,
80
- description: typeof parsed.description === 'string' ? parsed.description.trim() : undefined,
81
- };
82
- }
83
- catch {
84
- return {};
85
- }
86
- }
87
- function projectSlug(project) {
88
- const basename = path.basename(project) || 'agent';
89
- const slug = basename
90
- .normalize('NFKD')
91
- .replace(/[^a-zA-Z0-9-]+/g, '-')
92
- .replace(/^-+|-+$/g, '')
93
- .toLowerCase()
94
- .slice(0, 63)
95
- .replace(/-+$/g, '');
96
- return slug || 'agent';
97
- }
98
- /**
99
- * Resolve non-interactive creation defaults without performing any network or
100
- * filesystem mutation. The CLI uses this for both execution and --dry-run.
101
- */
102
- export function resolveAgentCreatePlan(options) {
103
- const aid = options.aid.trim();
104
- const project = path.resolve(options.project || process.cwd());
105
- const metadata = readProjectMetadata(project);
106
- const defaults = loadDefaults();
107
- const daemon = loadDaemonConfig();
108
- const configuredBaseagent = defaults?.active_baseagent;
109
- const available = detectAvailableBaseagents();
110
- const defaultBaseagent = configuredBaseagent && available.includes(configuredBaseagent)
111
- ? configuredBaseagent
112
- : pickDefaultBaseagent(available) ?? undefined;
113
- const baseagent = options.baseagent || defaultBaseagent;
114
- const baseagentSource = options.baseagent
115
- ? 'argument'
116
- : configuredBaseagent && baseagent === configuredBaseagent
117
- ? 'defaults'
118
- : 'detected';
119
- const owner = options.owner?.trim() || daemon.owners?.[0];
120
- const ownerSource = options.owner?.trim() ? 'argument' : owner ? 'daemon' : 'none';
121
- const name = options.name?.trim() || metadata.name || projectSlug(project);
122
- const description = options.description?.trim() || metadata.description || DEFAULT_AGENT_DESCRIPTION;
123
- const configExists = isValidAid(aid)
124
- && fs.existsSync(path.join(resolvePaths().agentsDir, aid, 'config.json'));
125
- return {
126
- aid,
127
- project,
128
- baseagent,
129
- owner,
130
- name,
131
- description,
132
- force: options.force === true,
133
- configExists,
134
- baseagentSource,
135
- ownerSource,
136
- nameSource: options.name?.trim() ? 'argument' : metadata.name ? 'package' : 'directory',
137
- descriptionSource: options.description?.trim() ? 'argument' : metadata.description ? 'package' : 'default',
138
- };
139
- }
140
- export function validateAgentCreatePlan(plan) {
141
- if (!isValidAid(plan.aid)) {
142
- return `Invalid AID "${plan.aid}": must be a valid multi-level domain (e.g. mybot.agentid.pub)`;
143
- }
144
- if (plan.configExists && !plan.force) {
145
- return `Agent "${plan.aid}" already exists (use --force to overwrite)`;
146
- }
147
- if (!plan.baseagent) {
148
- return 'No usable baseagent detected. Pass --baseagent or configure a baseagent in agents/defaults.json.';
149
- }
150
- if (!BASEAGENT_CANDIDATES.includes(plan.baseagent)) {
151
- return `Invalid baseagent: ${plan.baseagent} (options: ${BASEAGENT_CANDIDATES.join('/')})`;
152
- }
153
- if (!isBaseagentAvailable(plan.baseagent)) {
154
- return `${plan.baseagent} is not available in the current environment`;
155
- }
156
- if (plan.baseagent === 'ecagent') {
157
- const reason = ecagentUnavailableReason();
158
- if (reason)
159
- return reason;
160
- }
161
- if (plan.owner && !isValidAid(plan.owner)) {
162
- return `Invalid owner: ${plan.owner}`;
163
- }
164
- return null;
165
- }
166
- function ecagentUnavailableReason() {
167
- try {
168
- resolveEcagentConfig({ agents: { ecagent: {} } });
169
- return undefined;
170
- }
171
- catch (error) {
172
- return error instanceof Error ? error.message : String(error);
173
- }
43
+ function getAgentMdPath(aid) {
44
+ return getAgentMdPathFromPaths(aid);
174
45
  }
175
- function buildBaseagentsBlock(chosen) {
176
- return { [chosen]: {} };
177
- }
178
- const DEFAULT_CHATMODE = { private: 'interactive', group: 'proactive', nothuman: 'proactive' };
179
- /**
180
- * 写入新 agent 的初始配置(所有字段统一在 config.json)。
181
- * mentionMode 不写初始值,继承 schema 出厂默认(disabled)。
182
- */
183
- function saveInitialBehavior(aid, baseagent) {
184
- const behavior = {
185
- active_baseagent: baseagent,
186
- baseagents: buildBaseagentsBlock(baseagent),
187
- chatmode: { ...DEFAULT_CHATMODE },
188
- };
189
- const sel = { self: aid };
190
- const existingBehavior = cfgRead(ConfigTarget.Agent, sel) || {};
191
- cfgEnsure(ConfigTarget.Agent, sel);
192
- cfgWrite(ConfigTarget.Agent, { ...existingBehavior, ...behavior }, sel);
46
+ function toPosix(value) {
47
+ return value ? value.replace(/\\/g, '/') : null;
193
48
  }
194
49
  function readAgentMdIdentity(aid) {
195
- const agentMdFilePath = getAgentMdPathFromPaths(aid);
50
+ const file = getAgentMdPath(aid);
196
51
  try {
197
- if (!fs.existsSync(agentMdFilePath))
52
+ if (!fs.existsSync(file))
198
53
  return { name: null, description: null };
199
- const content = fs.readFileSync(agentMdFilePath, 'utf-8');
200
- const fmMatch = content.match(/^---\n([\s\S]*?)\n---/);
201
- if (!fmMatch)
54
+ const content = fs.readFileSync(file, 'utf8');
55
+ const frontmatter = content.match(/^---\n([\s\S]*?)\n---/);
56
+ if (!frontmatter)
202
57
  return { name: null, description: null };
203
- const fm = fmMatch[1];
204
- const nameMatch = fm.match(/^name:\s*["']?(.+?)["']?\s*$/m);
205
- const descMatch = fm.match(/^description:\s*["']?(.+?)["']?\s*$/m);
58
+ const body = frontmatter[1];
206
59
  return {
207
- name: nameMatch?.[1]?.trim() || null,
208
- description: descMatch?.[1]?.trim() || null,
60
+ name: body.match(/^name:\s*["']?(.+?)["']?\s*$/m)?.[1]?.trim() || null,
61
+ description: body.match(/^description:\s*["']?(.+?)["']?\s*$/m)?.[1]?.trim() || null,
209
62
  };
210
63
  }
211
64
  catch {
212
65
  return { name: null, description: null };
213
66
  }
214
67
  }
215
- function getAgentMdPath(aid) {
216
- return getAgentMdPathFromPaths(aid);
217
- }
218
- /** Normalize path separators to forward slashes for cross-platform consistent display. */
219
- function toPosix(p) {
220
- if (!p)
221
- return null;
222
- return p.replace(/\\/g, '/');
223
- }
68
+ // ==================== Helpers ====================
224
69
  // ==================== agentList ====================
225
70
  function toAgentListItem(info, stale = false) {
226
71
  const storedStatus = info.status || 'unknown';
@@ -360,499 +205,6 @@ export async function agentShow(aid) {
360
205
  ...(stale ? { stale: true } : {}),
361
206
  };
362
207
  }
363
- export async function agentCreateInteractive(opts = {}) {
364
- const p = resolvePaths();
365
- const ownRl = !opts.rl;
366
- const rl = opts.rl ?? readline.createInterface({
367
- input: opts.stdin || process.stdin,
368
- output: opts.stdout || process.stdout,
369
- });
370
- const ask = (q) => new Promise(r => rl.question(q, r));
371
- try {
372
- const { isValidAid, aidCreate } = await import('../aun/aid/index.js');
373
- const aidPrompt = opts.suggestedName
374
- ? `AID [${opts.suggestedName}]: `
375
- : 'AID (e.g. mybot.agentid.pub): ';
376
- let aid = '';
377
- while (!aid) {
378
- const aidInput = (await ask(aidPrompt)).trim();
379
- const candidate = aidInput || opts.suggestedName;
380
- if (!candidate) {
381
- console.log(' ⚠ AID is required.');
382
- continue;
383
- }
384
- if (!isValidAid(candidate)) {
385
- console.log(` ⚠ Invalid AID "${candidate}": must be a valid multi-level domain (e.g. mybot.agentid.pub)`);
386
- continue;
387
- }
388
- aid = candidate;
389
- }
390
- const agentDirPath = path.join(p.agentsDir, aid);
391
- const configExists = fs.existsSync(path.join(agentDirPath, 'config.json'));
392
- if (configExists) {
393
- const ans = (await ask(`Agent "${aid}" already exists. Overwrite config.json? (AID 与 agent.md 保留) [y/N]: `)).trim().toLowerCase();
394
- if (ans !== 'y' && ans !== 'yes') {
395
- return { ok: false, error: 'Aborted.' };
396
- }
397
- }
398
- console.log(`\nCreating agent: ${aid}\n`);
399
- let aidCreated = false;
400
- try {
401
- const result = await aidCreate(aid);
402
- try {
403
- await result.client.close();
404
- }
405
- catch { /* ignore */ }
406
- aidCreated = !result.alreadyExisted;
407
- console.log(` ✓ AID ${result.alreadyExisted ? 'reused' : 'created'}: ${aid}`);
408
- }
409
- catch (e) {
410
- return { ok: false, error: `AID creation failed: ${e?.message || e}` };
411
- }
412
- // Project path
413
- let suggestedProjectPath = '';
414
- try {
415
- const defaults = loadDefaults();
416
- const rootPath = agentProjectRootFromDefaults(defaults, p.root);
417
- suggestedProjectPath = deriveAgentProjectPath(rootPath, aid);
418
- }
419
- catch {
420
- suggestedProjectPath = deriveAgentProjectPath(agentProjectRootFromDefaults(null, p.root), aid);
421
- }
422
- const projectInput = (await ask(`Project path [${suggestedProjectPath}]: `)).trim();
423
- const projectPath = projectInput || suggestedProjectPath;
424
- if (!path.isAbsolute(projectPath)) {
425
- return { ok: false, error: 'Project path must be an absolute path.' };
426
- }
427
- if (!fs.existsSync(projectPath)) {
428
- const create = (await ask(`Project path does not exist. Create? [Y/n]: `)).trim().toLowerCase();
429
- if (create === '' || create === 'y' || create === 'yes') {
430
- try {
431
- fs.mkdirSync(projectPath, { recursive: true });
432
- }
433
- catch (e) {
434
- return { ok: false, error: `Failed to create project path "${projectPath}": ${e?.message || e}` };
435
- }
436
- console.log(` ✓ Created ${projectPath}`);
437
- }
438
- else {
439
- return { ok: false, error: 'Aborted.' };
440
- }
441
- }
442
- // Baseagent
443
- const available = detectAvailableBaseagents();
444
- if (available.length === 0 || (available.length === 1 && available[0] === 'ecagent' && ecagentUnavailableReason())) {
445
- return { ok: false, error: `No usable baseagent detected. Install claude/gemini CLI or codex CLI with app-server, or configure bundled ecagent.` };
446
- }
447
- const defaultBa = pickDefaultBaseagent(available);
448
- let baseagent;
449
- if (available.length === 1 && defaultBa) {
450
- console.log(` Baseagent: ${defaultBa}`);
451
- baseagent = defaultBa;
452
- }
453
- else {
454
- let chosen = null;
455
- while (chosen === null) {
456
- const defaultHint = defaultBa ? ` [${defaultBa}]` : '';
457
- const input = (await ask(`Baseagent (${available.join('/')})${defaultHint}: `)).trim() || defaultBa;
458
- if (!input) {
459
- console.log(' ecagent is optional; enter ecagent to select it explicitly.');
460
- continue;
461
- }
462
- if (!BASEAGENT_CANDIDATES.includes(input)) {
463
- console.log(` Invalid choice. Options: ${BASEAGENT_CANDIDATES.join('/')}`);
464
- continue;
465
- }
466
- if (!available.includes(input)) {
467
- console.log(` ${input} is not available in the current environment. Available: ${available.join('/')}`);
468
- continue;
469
- }
470
- if (input === 'ecagent') {
471
- const reason = ecagentUnavailableReason();
472
- if (reason) {
473
- console.log(` ecagent is unavailable: ${reason}`);
474
- continue;
475
- }
476
- }
477
- chosen = input;
478
- }
479
- baseagent = chosen;
480
- }
481
- // Owner: only prompt if daemon (daemon.json) has owners configured.
482
- // If daemon has no owner, agent owner is meaningless and should be skipped entirely.
483
- const { loadDaemonConfig } = await import('../config-store.js');
484
- const daemonCfg = loadDaemonConfig();
485
- const daemonHasOwner = daemonCfg.owners && daemonCfg.owners.length > 0;
486
- let owner;
487
- let skipQrBinding = false;
488
- if (daemonHasOwner) {
489
- const defaultOwner = daemonCfg.owners[0];
490
- while (true) {
491
- const prompt = `Owner AID [${defaultOwner}]: `;
492
- const ownerInput = (await ask(prompt)).trim();
493
- // User pressed Enter - use default owner
494
- if (!ownerInput) {
495
- owner = defaultOwner;
496
- break;
497
- }
498
- // User entered "new" or "qr" - trigger QR binding flow
499
- if (ownerInput.toLowerCase() === 'new' || ownerInput.toLowerCase() === 'qr') {
500
- owner = undefined;
501
- skipQrBinding = false;
502
- break;
503
- }
504
- // User entered a specific AID - validate and use it
505
- if (!isValidAid(ownerInput)) {
506
- console.log(` ⚠ Invalid Owner AID "${ownerInput}": must be a valid multi-level domain (e.g. alice.agentid.pub)`);
507
- console.log(` 提示: 直接回车使用 ${defaultOwner},输入 "new" 扫码绑定新 AID`);
508
- continue;
509
- }
510
- owner = ownerInput;
511
- skipQrBinding = true; // Skip QR if user manually entered an AID
512
- break;
513
- }
514
- }
515
- // Name + description for agent.md
516
- const defaultName = aid.split('.')[0];
517
- const agentName = (await ask(`Display name [${defaultName}]: `)).trim() || defaultName;
518
- const agentDescription = (await ask('Description (optional): ')).trim() || '';
519
- if (ownRl)
520
- rl.close();
521
- const agentConfig = withStaticOwner({
522
- $schema_version: CONFIG_SCHEMA_VERSION,
523
- aid,
524
- enabled: true,
525
- lifecycle: 'created',
526
- channels: [],
527
- projects: { defaultPath: projectPath },
528
- }, owner);
529
- saveAgent(agentConfig);
530
- ensureInitialRoleRegistry(aid);
531
- saveInitialBehavior(aid, baseagent);
532
- ensureAgentDirSkeleton(aid);
533
- // Generate and upload agent.md
534
- let agentmdUploaded = false;
535
- try {
536
- const { buildInitialAgentMd, agentmdPut } = await import('../aun/aid/index.js');
537
- const content = buildInitialAgentMd({ aid, name: agentName, description: agentDescription });
538
- const aunPath = process.env.AUN_HOME || defaultAunPath();
539
- // agentmdPut 会写本地文件到 agentMdPath(aid) 并调用 publishAgentMd
540
- // Upload with retry (3 attempts, 2s delay between retries)
541
- const MAX_ATTEMPTS = 3;
542
- const RETRY_DELAY_MS = 2000;
543
- let lastError;
544
- for (let attempt = 1; attempt <= MAX_ATTEMPTS; attempt++) {
545
- try {
546
- if (attempt > 1) {
547
- process.stdout.write(` ↻ agent.md 上传重试 (${attempt}/${MAX_ATTEMPTS})...\n`);
548
- await new Promise(r => setTimeout(r, RETRY_DELAY_MS));
549
- }
550
- await agentmdPut(content, { aid, aunPath });
551
- agentmdUploaded = true;
552
- break;
553
- }
554
- catch (e) {
555
- lastError = e;
556
- }
557
- }
558
- if (!agentmdUploaded) {
559
- console.warn(` ⚠ agent.md upload failed: ${lastError?.message || lastError}`);
560
- console.warn(` → Retry later with: ec aid agentmd put ${aid}`);
561
- }
562
- // Yield to allow the SDK WebSocket to fully close before IPC
563
- await new Promise(r => setTimeout(r, 0));
564
- }
565
- catch (e) {
566
- console.warn(` ⚠ agent.md generation failed: ${e?.message || e}`);
567
- }
568
- // Attempt hot-load via IPC (if daemon is running).
569
- // Cold-starting a new agent (connecting AUN WebSocket) routinely takes
570
- // >3s, so use a generous timeout to avoid a false "service not running"
571
- // report while the daemon actually finishes bringing the agent online.
572
- let hotLoaded = false;
573
- let hotLoadError;
574
- try {
575
- const ipcResult = await ipcQuery(p.socket, { type: 'evolagent.load', aid }, 30_000);
576
- if (ipcResult?.ok) {
577
- hotLoaded = true;
578
- }
579
- else if (ipcResult) {
580
- hotLoadError = ipcResult.error;
581
- }
582
- }
583
- catch { /* daemon not running */ }
584
- let ownerBoundAid;
585
- let ownerBindSkipped = false;
586
- let ownerBindInterrupted = false;
587
- // Only prompt for agent owner binding if:
588
- // 1. User didn't manually input owner during setup (owner is undefined)
589
- // 2. User didn't skip QR binding explicitly (skipQrBinding is false)
590
- // 3. We're in an interactive terminal
591
- // 4. The daemon (daemon.json) has at least one owner configured
592
- if (!owner && !skipQrBinding && process.stdin.isTTY && daemonHasOwner) {
593
- console.log('\n📡 Agent owner 绑定 — 请用 evol app 扫描二维码,10 分钟内有效\n');
594
- try {
595
- const { runAgentOwnerQrBindFlow } = await import('./init-channel.js');
596
- const bound = await runAgentOwnerQrBindFlow(aid, agentName);
597
- if (bound.action === 'bound') {
598
- ownerBoundAid = bound.boundAid;
599
- console.log(` ✓ 已配置 agent owner: ${bound.boundAid}`);
600
- }
601
- else if (bound.action === 'manual') {
602
- ownerBindSkipped = true;
603
- ownerBoundAid = await promptAgentOwnerManually(aid);
604
- }
605
- else {
606
- ownerBindInterrupted = true;
607
- }
608
- }
609
- catch (e) {
610
- ownerBindSkipped = true;
611
- console.log(` ⚠ 扫码绑定不可用: ${e?.message || e}`);
612
- ownerBoundAid = await promptAgentOwnerManually(aid);
613
- }
614
- }
615
- return {
616
- ok: true,
617
- aid,
618
- configPath: toPosix(path.join(agentDirPath, 'config.json')),
619
- aidCreated,
620
- agentmdUploaded,
621
- hotLoaded,
622
- hotLoadError,
623
- ownerBoundAid,
624
- ownerBindSkipped,
625
- ownerBindInterrupted,
626
- };
627
- }
628
- finally {
629
- if (ownRl) {
630
- try {
631
- rl.close();
632
- }
633
- catch { /* ignore */ }
634
- }
635
- }
636
- }
637
- async function promptAgentOwnerManually(aid) {
638
- const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
639
- try {
640
- while (true) {
641
- const raw = await new Promise(resolve => rl.question('\n扫码未完成。手动输入 agent owner AID(留空跳过): ', resolve));
642
- const owner = raw.trim();
643
- if (!owner) {
644
- console.log(' 已跳过 agent owner 配置');
645
- return undefined;
646
- }
647
- const { isValidAid } = await import('../aun/aid/index.js');
648
- if (!isValidAid(owner)) {
649
- console.log(' ⚠ Owner AID 格式无效(需合法多级域名,如 alice.agentid.pub)');
650
- continue;
651
- }
652
- const agent = loadAgent(aid);
653
- if (!agent) {
654
- console.log(` ⚠ 无法加载 agent 配置: ${aid}`);
655
- return undefined;
656
- }
657
- saveAgent(withStaticOwner(agent, owner));
658
- try {
659
- const result = await ipcQuery(resolvePaths().socket, { type: 'evolagent.reload', name: aid }, 30_000);
660
- if (result?.ok)
661
- console.log(' ✓ agent owner 已热重载');
662
- }
663
- catch { /* daemon not running */ }
664
- console.log(` ✓ 已配置 agent owner: ${owner}`);
665
- return owner;
666
- }
667
- }
668
- finally {
669
- rl.close();
670
- }
671
- }
672
- export async function agentCreateNonInteractive(opts, runtime = {}) {
673
- const p = resolvePaths();
674
- const { isValidAid, aidCreate } = await import('../aun/aid/index.js');
675
- opts.onPhase?.('validating', 'begin');
676
- /** 校验失败:透出 failed 进度并返回原结构(控制流不变)。 */
677
- const failValidating = (error) => {
678
- opts.onPhase?.('validating', 'failed', error);
679
- return { ok: false, error };
680
- };
681
- if (!isValidAid(opts.aid)) {
682
- return failValidating(`Invalid AID "${opts.aid}": must be a valid multi-level domain (e.g. mybot.agentid.pub)`);
683
- }
684
- const agentDirPath = path.join(p.agentsDir, opts.aid);
685
- const configExists = fs.existsSync(path.join(agentDirPath, 'config.json'));
686
- if (configExists && !opts.force) {
687
- return failValidating(`Agent "${opts.aid}" already exists: ${agentDirPath}/config.json (use --force to overwrite)`);
688
- }
689
- // Baseagent
690
- const available = detectAvailableBaseagents();
691
- if (available.length === 0 || (available.length === 1 && available[0] === 'ecagent' && ecagentUnavailableReason())) {
692
- return failValidating(`No usable baseagent detected. Install claude/gemini CLI or codex CLI with app-server, or configure bundled ecagent.`);
693
- }
694
- let baseagent;
695
- if (opts.baseagent) {
696
- if (!BASEAGENT_CANDIDATES.includes(opts.baseagent)) {
697
- return failValidating(`Invalid baseagent: ${opts.baseagent} (options: ${BASEAGENT_CANDIDATES.join('/')})`);
698
- }
699
- if (!available.includes(opts.baseagent)) {
700
- const reason = opts.baseagent === 'codex'
701
- ? getCodexAppServerAvailability().reason
702
- : undefined;
703
- return failValidating(reason || `${opts.baseagent} is not available in the current environment (available: ${available.join('/')})`);
704
- }
705
- if (opts.baseagent === 'ecagent') {
706
- const reason = ecagentUnavailableReason();
707
- if (reason)
708
- return failValidating(reason);
709
- }
710
- baseagent = opts.baseagent;
711
- }
712
- else {
713
- const defaultBaseagent = pickDefaultBaseagent(available);
714
- if (!defaultBaseagent)
715
- return failValidating('ecagent is optional; pass --baseagent ecagent after configuring its API key');
716
- baseagent = defaultBaseagent;
717
- }
718
- if (!path.isAbsolute(opts.project)) {
719
- return failValidating(`--project must be absolute: ${opts.project}`);
720
- }
721
- if (!fs.existsSync(opts.project)) {
722
- try {
723
- fs.mkdirSync(opts.project, { recursive: true });
724
- }
725
- catch (e) {
726
- return failValidating(`Failed to create ${opts.project}: ${e?.message || e}`);
727
- }
728
- }
729
- if (opts.owner && !isValidAid(opts.owner)) {
730
- return failValidating(`Invalid owner: ${opts.owner}`);
731
- }
732
- opts.onPhase?.('validating', 'done');
733
- // Register AID
734
- opts.onPhase?.('registering_aid', 'begin');
735
- let aidCreated = false;
736
- try {
737
- const result = await aidCreate(opts.aid);
738
- try {
739
- await result.client.close();
740
- }
741
- catch { /* ignore */ }
742
- aidCreated = !result.alreadyExisted;
743
- opts.onPhase?.('registering_aid', 'done', aidCreated ? 'created' : 'existed');
744
- }
745
- catch (e) {
746
- const error = `AID creation failed: ${e?.message || e}`;
747
- opts.onPhase?.('registering_aid', 'failed', error);
748
- return { ok: false, error };
749
- }
750
- // Force 模式下若 agent 已存在,保留生命周期(避免重复 bootstrap/welcome)。
751
- let preservedLifecycle = 'created';
752
- if (configExists) {
753
- try {
754
- const existing = loadAgent(opts.aid);
755
- if (existing?.lifecycle)
756
- preservedLifecycle = existing.lifecycle;
757
- }
758
- catch { /* ignore */ }
759
- }
760
- const agentConfig = withStaticOwner({
761
- $schema_version: CONFIG_SCHEMA_VERSION,
762
- aid: opts.aid,
763
- enabled: true,
764
- lifecycle: preservedLifecycle,
765
- channels: [],
766
- projects: { defaultPath: opts.project },
767
- }, opts.owner);
768
- opts.onPhase?.('config_saved', 'begin');
769
- saveAgent(agentConfig);
770
- ensureInitialRoleRegistry(opts.aid);
771
- saveInitialBehavior(opts.aid, baseagent);
772
- ensureAgentDirSkeleton(opts.aid);
773
- opts.onPhase?.('config_saved', 'done');
774
- // Generate and upload agent.md
775
- opts.onPhase?.('uploading_agentmd', 'begin');
776
- let agentmdUploaded = false;
777
- try {
778
- const { buildInitialAgentMd, agentmdPut } = await import('../aun/aid/index.js');
779
- const content = buildInitialAgentMd({
780
- aid: opts.aid,
781
- name: opts.name,
782
- description: opts.description,
783
- });
784
- const aunPath = process.env.AUN_HOME || defaultAunPath();
785
- // agentmdPut 会写本地文件到 agentMdPath(aid) 并调用 publishAgentMd
786
- const MAX_ATTEMPTS = 3;
787
- const RETRY_DELAY_MS = 2000;
788
- let lastError;
789
- for (let attempt = 1; attempt <= MAX_ATTEMPTS; attempt++) {
790
- try {
791
- if (attempt > 1)
792
- await new Promise(r => setTimeout(r, RETRY_DELAY_MS));
793
- await agentmdPut(content, { aid: opts.aid, aunPath });
794
- agentmdUploaded = true;
795
- opts.onPhase?.('uploading_agentmd', 'done');
796
- break;
797
- }
798
- catch (e) {
799
- lastError = e;
800
- }
801
- }
802
- if (!agentmdUploaded) {
803
- console.warn(`⚠ agent.md upload failed: ${lastError?.message || lastError}`);
804
- console.warn(` Retry later with: ec aid agentmd put ${opts.aid}`);
805
- opts.onPhase?.('uploading_agentmd', 'warn', `upload failed: ${lastError?.message || lastError}`);
806
- }
807
- await new Promise(r => setTimeout(r, 0));
808
- }
809
- catch (e) {
810
- console.warn(`⚠ agent.md generation failed: ${e?.message || e}`);
811
- opts.onPhase?.('uploading_agentmd', 'warn', `generation failed: ${e?.message || e}`);
812
- }
813
- // Core-owned callers invoke the already-loaded runtime directly. Standalone
814
- // CLI callers retain the IPC path so this module remains usable without a
815
- // daemon instance in the current process.
816
- let hotLoaded = false;
817
- let hotLoadError;
818
- opts.onPhase?.('hot_loading', 'begin');
819
- try {
820
- if (runtime.hotLoadAgent) {
821
- await runtime.hotLoadAgent(opts.aid);
822
- hotLoaded = true;
823
- opts.onPhase?.('hot_loading', 'done');
824
- }
825
- else {
826
- const ipcResult = await ipcQuery(p.socket, { type: 'evolagent.load', aid: opts.aid }, 30_000);
827
- if (ipcResult?.ok) {
828
- hotLoaded = true;
829
- opts.onPhase?.('hot_loading', 'done');
830
- }
831
- else if (ipcResult) {
832
- hotLoadError = ipcResult.error;
833
- opts.onPhase?.('hot_loading', 'warn', hotLoadError);
834
- }
835
- else {
836
- opts.onPhase?.('hot_loading', 'warn', 'daemon not running');
837
- }
838
- }
839
- }
840
- catch (error) {
841
- const message = error instanceof Error ? error.message : String(error);
842
- hotLoadError = message;
843
- opts.onPhase?.('hot_loading', 'warn', message);
844
- }
845
- return {
846
- ok: true,
847
- aid: opts.aid,
848
- ownerAid: opts.owner,
849
- configPath: toPosix(path.join(agentDirPath, 'config.json')),
850
- aidCreated,
851
- agentmdUploaded,
852
- hotLoaded,
853
- hotLoadError,
854
- };
855
- }
856
208
  // ==================== agentSyncAids (deprecated) ====================
857
209
  /** @deprecated sync-aids 已废弃,不再从 CLI 调用 */
858
210
  export async function agentSyncAids() {
@@ -930,32 +282,32 @@ export async function agentReload(aid, options = {}) {
930
282
  }
931
283
  // Full resync
932
284
  try {
933
- const result = await ipcQuery(p.socket, { type: 'evolagent.resync' }, AGENT_RESYNC_TIMEOUT_MS);
285
+ const result = await ipcQuery(p.socket, { type: 'evolagent.resync' }, AGENT_RESYNC_TIMEOUT_MS, { throwOnError: true });
934
286
  if (result === null) {
935
- return { ok: false, error: 'evolcore 未运行,请先 ec start' };
287
+ return { ok: false, code: 'IPC_INVALID_RESPONSE', error: 'daemon IPC 未返回有效响应,请检查 ec status' };
936
288
  }
937
289
  if (result?.ok) {
938
290
  return { ok: true, results: result.results || [] };
939
291
  }
940
292
  return { ok: false, error: result?.error || 'unknown error' };
941
293
  }
942
- catch {
943
- return { ok: false, error: 'evolcore 未运行,请先 ec start' };
294
+ catch (error) {
295
+ return { ok: false, code: error instanceof IpcTransportError ? `IPC_${error.code}` : 'IPC_FAILED', error: String(error instanceof Error ? error.message : error) };
944
296
  }
945
297
  }
946
298
  // Single agent reload
947
299
  try {
948
- const result = await ipcQuery(p.socket, { type: 'evolagent.reload', name: aid, force: options.force === true }, AGENT_RELOAD_TIMEOUT_MS);
300
+ const result = await ipcQuery(p.socket, { type: 'evolagent.reload', name: aid, force: options.force === true }, AGENT_RELOAD_TIMEOUT_MS, { throwOnError: true });
949
301
  if (result === null) {
950
- return { ok: false, error: 'evolcore 未运行,请先 ec start 后再 reload' };
302
+ return { ok: false, code: 'IPC_INVALID_RESPONSE', error: 'daemon IPC 未返回有效响应,请检查 ec status' };
951
303
  }
952
304
  if (result?.ok) {
953
305
  return { ok: true };
954
306
  }
955
307
  return { ok: false, code: result?.code, error: result?.error || 'unknown error' };
956
308
  }
957
- catch {
958
- return { ok: false, error: 'evolcore 未运行,请先 ec start 后再 reload' };
309
+ catch (error) {
310
+ return { ok: false, code: error instanceof IpcTransportError ? `IPC_${error.code}` : 'IPC_FAILED', error: String(error instanceof Error ? error.message : error) };
959
311
  }
960
312
  }
961
313
  // ==================== agentEnable / agentDisable ====================
@@ -1105,6 +457,107 @@ export async function agentSet(aid, key, rawValue, options = {}) {
1105
457
  catch { }
1106
458
  return { ok: true, aid, key, value, reloaded };
1107
459
  }
460
+ export const AGENT_PERSONA_MAX_BYTES = 64 * 1024;
461
+ function validatePersonaContent(content) {
462
+ if (content.includes('\0'))
463
+ return 'persona 只能包含文本内容';
464
+ if (Buffer.byteLength(content, 'utf8') > AGENT_PERSONA_MAX_BYTES) {
465
+ return `persona 不能超过 ${AGENT_PERSONA_MAX_BYTES} bytes`;
466
+ }
467
+ return null;
468
+ }
469
+ function readPersonaSource(file) {
470
+ const resolved = path.resolve(file);
471
+ if (!/\.(?:md|txt)$/i.test(resolved)) {
472
+ return { ok: false, code: 'INVALID_ARGS', error: '--file 只接受 .md 或 .txt 文本文件' };
473
+ }
474
+ try {
475
+ const stat = fs.statSync(resolved);
476
+ if (!stat.isFile())
477
+ return { ok: false, code: 'INVALID_ARGS', error: `--file 必须指向普通文件: ${resolved}` };
478
+ const content = fs.readFileSync(resolved, 'utf8');
479
+ const error = validatePersonaContent(content);
480
+ return error ? { ok: false, code: 'INVALID_ARGS', error } : { ok: true, content };
481
+ }
482
+ catch (e) {
483
+ return { ok: false, code: 'READ_FAILED', error: `无法读取 --file: ${e?.message || e}` };
484
+ }
485
+ }
486
+ /** Read or replace the current Agent's personal/persona.md. */
487
+ export async function agentPersona(aid, options = {}) {
488
+ if (options.text !== undefined && options.file !== undefined)
489
+ return { ok: false, code: 'INVALID_ARGS', error: '--text 与 --file 不能同时使用' };
490
+ const managedSession = !!process.env.EVOLCORE_SESSION_ID && !options.trustedLocal;
491
+ let text = options.text;
492
+ if (options.file !== undefined) {
493
+ if (managedSession) {
494
+ const resolved = path.resolve(options.file);
495
+ const projectRoot = path.resolve(process.cwd());
496
+ const tempRoot = process.env.TMPDIR?.trim() && path.isAbsolute(process.env.TMPDIR)
497
+ ? path.resolve(process.env.TMPDIR)
498
+ : undefined;
499
+ const allowed = isSameOrDescendant(resolved, projectRoot)
500
+ || (!!tempRoot && isSameOrDescendant(resolved, tempRoot));
501
+ if (!allowed || isHClassPath(resolved) || isLClassPath(resolved)) {
502
+ return { ok: false, code: 'INVALID_ARGS', error: '--file 必须位于当前项目或会话 $TMPDIR,且不能读取 EvolCore 受保护路径' };
503
+ }
504
+ try {
505
+ const stat = fs.lstatSync(resolved);
506
+ if (stat.isSymbolicLink() || !stat.isFile()) {
507
+ return { ok: false, code: 'INVALID_ARGS', error: '--file 必须指向当前项目或会话 $TMPDIR 内的普通文件' };
508
+ }
509
+ const realPath = fs.realpathSync(resolved);
510
+ const realProjectRoot = fs.realpathSync(projectRoot);
511
+ const realTempRoot = tempRoot ? fs.realpathSync(tempRoot) : undefined;
512
+ const realAllowed = isSameOrDescendant(realPath, realProjectRoot)
513
+ || (!!realTempRoot && isSameOrDescendant(realPath, realTempRoot));
514
+ if (!realAllowed || isHClassPath(realPath) || isLClassPath(realPath)) {
515
+ return { ok: false, code: 'INVALID_ARGS', error: '--file 解析后越出允许目录或命中 EvolCore 受保护路径' };
516
+ }
517
+ }
518
+ catch (e) {
519
+ return { ok: false, code: 'READ_FAILED', error: `无法校验 --file: ${e?.message || e}` };
520
+ }
521
+ }
522
+ const source = readPersonaSource(options.file);
523
+ if (!source.ok)
524
+ return source;
525
+ text = source.content;
526
+ }
527
+ if (text !== undefined) {
528
+ const error = validatePersonaContent(text);
529
+ if (error)
530
+ return { ok: false, code: 'INVALID_ARGS', error };
531
+ }
532
+ const managedArgs = options.text !== undefined
533
+ ? ['persona', '--text', text]
534
+ : options.file !== undefined
535
+ ? ['persona', '--text', text]
536
+ : ['persona', 'show'];
537
+ if (managedSession) {
538
+ const managed = await runManagedAgentOperation(managedArgs);
539
+ if (!managed.ok)
540
+ return managed;
541
+ return { ok: true, aid, content: managed.content ?? null, ...(managed.path ? { path: managed.path } : {}), ...(managed.saved ? { saved: true } : {}) };
542
+ }
543
+ const personaPath = path.join(agentPersonalDir(aid), 'persona.md');
544
+ if (text === undefined) {
545
+ try {
546
+ return { ok: true, aid, content: fs.existsSync(personaPath) ? fs.readFileSync(personaPath, 'utf8') : null, path: personaPath };
547
+ }
548
+ catch (e) {
549
+ return { ok: false, code: 'READ_FAILED', error: e?.message || String(e) };
550
+ }
551
+ }
552
+ try {
553
+ fs.mkdirSync(path.dirname(personaPath), { recursive: true });
554
+ fs.writeFileSync(personaPath, text, 'utf8');
555
+ return { ok: true, aid, content: text, path: personaPath, saved: true };
556
+ }
557
+ catch (e) {
558
+ return { ok: false, code: 'WRITE_FAILED', error: e?.message || String(e) };
559
+ }
560
+ }
1108
561
  /**
1109
562
  * Add or overwrite a channel instance on a per-agent config.
1110
563
  *