evolcore 0.0.25 → 0.0.27

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 (304) hide show
  1. package/CHANGELOG.md +632 -565
  2. package/LICENSE +21 -21
  3. package/README.md +455 -455
  4. package/bin/codex-managed-acl.ps1 +203 -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-availability.js +68 -0
  10. package/dist/agents/baseagent.js +96 -46
  11. package/dist/agents/claude-runner.js +172 -33
  12. package/dist/agents/codex/app-server-client.js +76 -31
  13. package/dist/agents/codex/catalog.js +1 -1
  14. package/dist/agents/codex/diagnostics.js +34 -1
  15. package/dist/agents/codex-runner.js +434 -446
  16. package/dist/agents/ecagent-runner.js +174 -34
  17. package/dist/agents/gemini-runner.js +8 -7
  18. package/dist/agents/runner-types.js +75 -19
  19. package/dist/aun/aid/agentmd.js +151 -36
  20. package/dist/aun/aid/client.js +1 -1
  21. package/dist/aun/aid/control-aid.js +180 -1
  22. package/dist/aun/aid/identity.js +4 -4
  23. package/dist/aun/aid/index.js +1 -1
  24. package/dist/aun/aid/store.js +17 -13
  25. package/dist/aun/group/history.js +303 -0
  26. package/dist/aun/group/identity.js +43 -0
  27. package/dist/aun/group/index.js +5 -0
  28. package/dist/aun/{msg/group.js → group/operations.js} +162 -39
  29. package/dist/aun/{menu-client.js → menu/client.js} +85 -6
  30. package/dist/aun/menu/index.js +2 -0
  31. package/dist/aun/menu/transport.js +49 -0
  32. package/dist/aun/msg/index.js +2 -1
  33. package/dist/aun/msg/managed-operation.js +85 -19
  34. package/dist/aun/msg/mention-schema.js +51 -0
  35. package/dist/aun/msg/p2p.js +19 -9
  36. package/dist/aun/msg/upload.js +43 -80
  37. package/dist/aun/outbox.js +29 -7
  38. package/dist/aun/rpc/client.js +1 -0
  39. package/dist/aun/storage/client.js +39 -50
  40. package/dist/channels/aun.js +842 -318
  41. package/dist/channels/daemon.js +10 -6
  42. package/dist/cli/agent-command.js +212 -89
  43. package/dist/cli/agent-create.js +626 -11
  44. package/dist/cli/agent.js +125 -13
  45. package/dist/cli/aun-commands.js +487 -337
  46. package/dist/cli/bench.js +20 -20
  47. package/dist/cli/config.js +55 -33
  48. package/dist/cli/contact.js +72 -15
  49. package/dist/cli/ctl-command.js +24 -24
  50. package/dist/cli/daemon-commands.js +140 -63
  51. package/dist/cli/diag.js +31 -0
  52. package/dist/cli/fs-command.js +70 -70
  53. package/dist/cli/handoff-command.js +46 -14
  54. package/dist/cli/index.js +91 -79
  55. package/dist/cli/init.js +34 -1
  56. package/dist/cli/link-rules.js +21 -21
  57. package/dist/cli/managed-command-guard.js +5 -1
  58. package/dist/cli/menu-client.js +51 -32
  59. package/dist/cli/model.js +3 -3
  60. package/dist/cli/net-check.js +22 -22
  61. package/dist/cli/queue-command.js +31 -31
  62. package/dist/cli/response.js +32 -32
  63. package/dist/cli/restart-monitor.js +103 -42
  64. package/dist/cli/stats.js +51 -51
  65. package/dist/cli/task-command.js +59 -0
  66. package/dist/cli/task-context.js +97 -49
  67. package/dist/cli/trigger-command.js +74 -64
  68. package/dist/config/builtin-roles.js +11 -0
  69. package/dist/config/config-field-policy.js +11 -0
  70. package/dist/config/config-manager.js +32 -4
  71. package/dist/config/config-operation-service.js +160 -0
  72. package/dist/config/contact-book-store.js +1 -1
  73. package/dist/config/contact-book.js +1 -1
  74. package/dist/config/contact-operation-service.js +97 -8
  75. package/dist/config/peer-role-resolver.js +1 -1
  76. package/dist/config/resolved-config-op.js +30 -7
  77. package/dist/config-store.js +1 -1
  78. package/dist/core/agent-application-service.js +7 -1
  79. package/dist/core/audit/event-key.js +17 -2
  80. package/dist/core/audit/log-integrity.js +34 -8
  81. package/dist/core/auth/agent-delegation.js +48 -14
  82. package/dist/core/auth/auth-gateway.js +2 -0
  83. package/dist/core/auth/authorization-audit.js +53 -49
  84. package/dist/core/auth/operation-authorizer.js +90 -7
  85. package/dist/core/auth/operation-catalog.js +145 -4
  86. package/dist/core/auth/remote-access-store.js +155 -10
  87. package/dist/core/bootstrap-messages.js +66 -7
  88. package/dist/core/bootstrap-service.js +111 -20
  89. package/dist/core/command/agent-control.js +88 -7
  90. package/dist/core/command/agent-storage-menu.js +443 -0
  91. package/dist/core/command/cli-intent-parser.js +19 -0
  92. package/dist/core/command/command-handler.js +159 -65
  93. package/dist/core/command/data-root-resolver.js +273 -0
  94. package/dist/core/command/diag-service.js +118 -0
  95. package/dist/core/command/group-menu.js +7 -2
  96. package/dist/core/command/menu-catalog.js +46 -7
  97. package/dist/core/command/menu-handler.js +392 -100
  98. package/dist/core/command/menu-protocol.js +45 -6
  99. package/dist/core/command/menu-token-store.js +41 -8
  100. package/dist/core/command/role-menu.js +2 -2
  101. package/dist/core/command/slash-gate.js +34 -4
  102. package/dist/core/command/slash-handler.js +123 -43
  103. package/dist/core/event-catalog.js +4 -0
  104. package/dist/core/evolagent-registry.js +28 -2
  105. package/dist/core/group/invite-service.js +924 -0
  106. package/dist/core/handoff/runtime.js +205 -27
  107. package/dist/core/handoff/store.js +193 -4
  108. package/dist/core/interaction-router.js +22 -8
  109. package/dist/core/message/im-renderer.js +146 -34
  110. package/dist/core/message/message-bridge.js +163 -20
  111. package/dist/core/message/message-log.js +5 -3
  112. package/dist/core/message/message-queue.js +99 -41
  113. package/dist/core/message/message-utils.js +1 -0
  114. package/dist/core/message/response-engine.js +755 -144
  115. package/dist/core/model/catalog-service.js +79 -49
  116. package/dist/core/model/context-window-suffix.js +33 -0
  117. package/dist/core/model/model-catalog.js +1 -1
  118. package/dist/core/model/model-permission.js +12 -3
  119. package/dist/core/permission/approval-gateway.js +36 -3
  120. package/dist/core/permission/ec-command-parser.js +112 -41
  121. package/dist/core/permission/protected-paths.js +134 -16
  122. package/dist/core/permission/sandbox-runtime.js +108 -17
  123. package/dist/core/permission/tool-error-code.js +15 -0
  124. package/dist/core/permission/tool-policy.js +9 -8
  125. package/dist/core/relation/peer-identity.js +57 -6
  126. package/dist/core/relation/shared-data.js +106 -0
  127. package/dist/core/role/runtime-policy.js +64 -0
  128. package/dist/core/runtime-lock.js +10 -26
  129. package/dist/core/session/runner-task-history.js +170 -0
  130. package/dist/core/session/session-manager.js +34 -6
  131. package/dist/core/session/session-mapper.js +41 -0
  132. package/dist/core/session/session-turn-coordinator.js +37 -7
  133. package/dist/core/session/session-turns.js +48 -7
  134. package/dist/core/task/task-recovery-service.js +176 -0
  135. package/dist/core/task/task-resume-operation.js +32 -0
  136. package/dist/eck/group-rules-sync.js +5 -3
  137. package/dist/eck/kit-renderer.js +1 -0
  138. package/dist/index.js +588 -217
  139. package/dist/ipc.js +193 -15
  140. package/dist/paths.js +17 -4
  141. package/dist/product.js +2 -0
  142. package/dist/response-system/engines/v1/proactive-flow.js +59 -2
  143. package/dist/stats/billing.js +41 -15
  144. package/dist/stats/db.js +159 -159
  145. package/dist/stats/eck-vars.js +9 -9
  146. package/dist/stats/price-resolver.js +28 -10
  147. package/dist/stats/query.js +139 -139
  148. package/dist/stats/role-budget.js +6 -6
  149. package/dist/stats/writer.js +51 -51
  150. package/dist/trigger/event-source.js +19 -0
  151. package/dist/trigger/feedback.js +233 -40
  152. package/dist/trigger/parser.js +55 -5
  153. package/dist/trigger/patch.js +9 -0
  154. package/dist/trigger/scheduler.js +30 -4
  155. package/dist/trigger/validation.js +20 -3
  156. package/dist/utils/cross-platform.js +30 -1
  157. package/dist/utils/ecweb-utils.js +1 -0
  158. package/dist/utils/error-dict.json +160 -160
  159. package/dist/utils/error-utils.js +61 -2
  160. package/dist/utils/evolcore-version.js +129 -0
  161. package/dist/utils/logger.js +26 -14
  162. package/dist/utils/macos-autostart.js +22 -22
  163. package/dist/utils/model-prices.jsonl +23 -23
  164. package/dist/utils/npm-ops.js +176 -35
  165. package/dist/utils/tool-summary.js +59 -3
  166. package/dist/utils/welcome.js +196 -23
  167. package/ecagent/LICENSE +21 -21
  168. package/ecagent/NOTICE +11 -11
  169. package/ecagent/README.md +61 -61
  170. package/ecagent/dist/harness/agent-harness.js +3 -0
  171. package/ecagent/dist/harness/compaction/branch-summarization.js +30 -30
  172. package/ecagent/dist/harness/compaction/compaction.js +83 -83
  173. package/ecagent/dist/harness/messages.js +7 -7
  174. package/ecagent/dist/harness/session/jsonl-storage.js +21 -2
  175. package/kits/docs/GUIDE.md +20 -20
  176. package/kits/docs/INDEX.md +60 -66
  177. package/kits/docs/aun/CHEATSHEET.md +19 -19
  178. package/kits/docs/aun/SYNC_PROTOCOL.md +15 -15
  179. package/kits/docs/channels/aun.md +69 -56
  180. package/kits/docs/channels/feishu.md +56 -56
  181. package/kits/docs/context-assembly.md +379 -367
  182. package/kits/docs/eck_templates/GUIDE.template.md +22 -22
  183. package/kits/docs/eck_templates/INDEX.template.md +28 -28
  184. package/kits/docs/eck_templates/path-registry.template.md +33 -33
  185. package/kits/docs/eck_templates/runtime.template.md +19 -19
  186. package/kits/docs/evolcore/INDEX.md +76 -71
  187. package/kits/docs/evolcore/agent-create.md +202 -165
  188. package/kits/docs/evolcore/agent.md +99 -67
  189. package/kits/docs/evolcore/aid.md +68 -60
  190. package/kits/docs/evolcore/codex-windows-sandbox.md +146 -146
  191. package/kits/docs/evolcore/config.md +241 -195
  192. package/kits/docs/evolcore/contact.md +75 -68
  193. package/kits/docs/evolcore/ctl.md +46 -46
  194. package/kits/docs/evolcore/event.md +216 -216
  195. package/kits/docs/evolcore/fs-architecture.md +1215 -1215
  196. package/kits/docs/evolcore/fs.md +110 -110
  197. package/kits/docs/evolcore/group-collaboration.md +244 -244
  198. package/kits/docs/evolcore/group-fs.md +17 -17
  199. package/kits/docs/evolcore/group-rules.md +251 -251
  200. package/kits/docs/evolcore/group.md +267 -152
  201. package/kits/docs/evolcore/menu-client.md +58 -34
  202. package/kits/docs/evolcore/model.md +50 -50
  203. package/kits/docs/evolcore/msg.md +169 -165
  204. package/kits/docs/evolcore/response.md +75 -75
  205. package/kits/docs/evolcore/rpc.md +37 -37
  206. package/kits/docs/evolcore/self-summary.md +29 -29
  207. package/kits/docs/evolcore/stats.md +83 -83
  208. package/kits/docs/evolcore/storage.md +53 -50
  209. package/kits/docs/evolcore/task.md +14 -0
  210. package/kits/docs/evolcore/trigger.md +623 -575
  211. package/kits/docs/identity/AID_PROFILE_SPEC.md +26 -26
  212. package/kits/docs/identity/PATH_OPS.md +16 -16
  213. package/kits/docs/identity/ROLE_DETAIL.md +25 -25
  214. package/kits/docs/identity/identity-tools.md +26 -26
  215. package/kits/docs/path-registry.md +62 -62
  216. package/kits/docs/prompt-loading-architecture.md +269 -269
  217. package/kits/docs/venues/aun-group.md +45 -45
  218. package/kits/docs/venues/aun-private.md +10 -10
  219. package/kits/docs/venues/client-desktop.md +10 -10
  220. package/kits/docs/venues/client-mobile.md +10 -10
  221. package/kits/docs/venues/feishu-group.md +13 -13
  222. package/kits/docs/venues/feishu-private.md +9 -9
  223. package/kits/docs/venues/group.md +42 -25
  224. package/kits/docs/venues/private.md +10 -10
  225. package/kits/eck_manifest.auxiliary.json +43 -43
  226. package/kits/eck_manifest.json +217 -190
  227. package/kits/eck_message_manifest.json +74 -74
  228. package/kits/migrations/README-role-config-v4.md +32 -32
  229. package/kits/migrations/migrate-contact-book-v2.mjs +748 -748
  230. package/kits/migrations/migrate-role-config-v4.mjs +614 -614
  231. package/kits/migrations/migrate-role-config-v5.mjs +365 -365
  232. package/kits/migrations/rename-config-file.mjs +99 -99
  233. package/kits/rules/01-overview.md +166 -162
  234. package/kits/rules/02-navigation.md +96 -96
  235. package/kits/rules/03-identity.md +36 -36
  236. package/kits/rules/04-relation.md +76 -75
  237. package/kits/rules/05-venue.md +60 -60
  238. package/kits/rules/06-channel.md +65 -63
  239. package/kits/schemas/_meta.json +53 -53
  240. package/kits/schemas/agent-config.schema.1.json +177 -177
  241. package/kits/schemas/agent-config.schema.10.json +395 -395
  242. package/kits/schemas/agent-config.schema.11.json +421 -421
  243. package/kits/schemas/agent-config.schema.12.json +451 -427
  244. package/kits/schemas/agent-config.schema.2.json +239 -239
  245. package/kits/schemas/agent-config.schema.3.json +119 -119
  246. package/kits/schemas/agent-config.schema.4.json +208 -208
  247. package/kits/schemas/agent-config.schema.5.json +326 -326
  248. package/kits/schemas/agent-config.schema.6.json +322 -322
  249. package/kits/schemas/agent-config.schema.7.json +303 -303
  250. package/kits/schemas/agent-config.schema.8.json +304 -304
  251. package/kits/schemas/agent-config.schema.9.json +364 -364
  252. package/kits/schemas/contact-book.schema.1.json +36 -36
  253. package/kits/schemas/contact-book.schema.2.json +43 -43
  254. package/kits/schemas/contact-book.schema.3.json +67 -67
  255. package/kits/schemas/daemon.schema.1.json +91 -91
  256. package/kits/schemas/daemon.schema.2.json +101 -101
  257. package/kits/schemas/daemon.schema.3.json +123 -123
  258. package/kits/schemas/daemon.schema.4.json +136 -136
  259. package/kits/schemas/daemon.schema.5.json +135 -135
  260. package/kits/schemas/daemon.schema.6.json +131 -131
  261. package/kits/schemas/defaults.schema.1.json +81 -81
  262. package/kits/schemas/defaults.schema.2.json +85 -85
  263. package/kits/schemas/defaults.schema.3.json +73 -73
  264. package/kits/schemas/defaults.schema.4.json +107 -107
  265. package/kits/schemas/defaults.schema.5.json +119 -119
  266. package/kits/schemas/menu-exec-schema-commands.md +208 -208
  267. package/kits/schemas/migrations/README.md +30 -30
  268. package/kits/schemas/relation-config.schema.1.json +158 -158
  269. package/kits/schemas/relation-config.schema.2.json +73 -73
  270. package/kits/schemas/relation-config.schema.3.json +50 -50
  271. package/kits/schemas/relation-config.schema.4.json +47 -47
  272. package/kits/schemas/relation-config.schema.5.json +47 -47
  273. package/kits/schemas/relation-config.schema.6.json +46 -46
  274. package/kits/schemas/relation-config.schema.7.json +59 -59
  275. package/kits/schemas/relation-config.schema.8.json +103 -103
  276. package/kits/schemas/role-config.schema.1.json +215 -201
  277. package/kits/schemas/role-registry.schema.1.json +35 -35
  278. package/kits/schemas/single-session.schema.1.json +31 -31
  279. package/kits/schemas/single-session.schema.2.json +57 -57
  280. package/kits/schemas/single-session.schema.3.json +32 -32
  281. package/kits/templates/bootstrap-welcome.md +17 -17
  282. package/kits/templates/message-fragments/handoff-context-to-target.md +9 -9
  283. package/kits/templates/message-fragments/handoff-request-to-target.md +19 -19
  284. package/kits/templates/message-fragments/handoff-response-to-origin.md +9 -9
  285. package/kits/templates/message-fragments/inject-default.md +2 -2
  286. package/kits/templates/message-fragments/item.md +2 -2
  287. package/kits/templates/roles/admin.json +92 -92
  288. package/kits/templates/roles/member.json +101 -96
  289. package/kits/templates/roles/owner.json +9 -9
  290. package/kits/templates/roles/visitor.json +90 -85
  291. package/kits/templates/system-fragments/baseagent.md +14 -14
  292. package/kits/templates/system-fragments/bootstrap.md +53 -34
  293. package/kits/templates/system-fragments/channel.md +28 -64
  294. package/kits/templates/system-fragments/commands.md +36 -28
  295. package/kits/templates/system-fragments/group-unread-history.md +7 -0
  296. package/kits/templates/system-fragments/identity.md +13 -13
  297. package/kits/templates/system-fragments/relation.md +19 -19
  298. package/kits/templates/system-fragments/reply.md +56 -0
  299. package/kits/templates/system-fragments/session.md +59 -59
  300. package/kits/templates/system-fragments/venue.md +31 -31
  301. package/package.json +104 -98
  302. package/dist/aun/group-identity.js +0 -9
  303. /package/dist/aun/{group-fs-download.js → group/fs-download.js} +0 -0
  304. /package/dist/aun/{msg/group-index.js → group/index-rpc.js} +0 -0
@@ -1,18 +1,34 @@
1
1
  import fs from 'fs';
2
+ import { randomBytes, createHash } from 'crypto';
2
3
  import path from 'path';
3
4
  import readline from 'readline';
4
- import { resolvePaths, aunPath as defaultAunPath } from '../paths.js';
5
+ import { parse } from 'yaml';
6
+ import { resolvePaths, aunPath as defaultAunPath, agentConfig, agentDataDir, agentPersonalDir, aidLocalDir, agentMdPath } from '../paths.js';
5
7
  import { loadDefaults, loadAgent, saveAgent, ensureAgentDirSkeleton, loadDaemonConfig } from '../config-store.js';
6
8
  import { ConfigTarget, write as cfgWrite, ensureFile as cfgEnsure, read as cfgRead } from '../config/config-manager.js';
7
9
  import { ipcQuery } from '../ipc.js';
8
10
  import { CONFIG_SCHEMA_VERSION } from '../types.js';
9
11
  import { ensureInitialRoleRegistry } from '../config/role-service.js';
12
+ import { addContact } from '../config/contact-request-service.js';
10
13
  import { getCodexAppServerAvailability } from '../agents/codex-runner.js';
11
14
  import { resolveEcagentConfig } from '../agents/baseagent.js';
12
15
  import { AUTO_BASEAGENT_CANDIDATES, BASEAGENT_CANDIDATES, detectAvailableBaseagents, } from '../agents/baseagent-availability.js';
13
16
  import { agentProjectRootFromDefaults, deriveAgentProjectPath } from '../utils/project-path.js';
14
17
  import { isValidAid } from '../aun/aid/validation.js';
15
- import { AGENT_TAG_MAX_COUNT, AGENT_TAG_MIN_COUNT, DEFAULT_AGENT_DESCRIPTION, validateAgentTags, } from '../aun/aid/agentmd.js';
18
+ import { AGENT_DESCRIPTION_MAX_LENGTH, AGENT_TAG_MAX_COUNT, AGENT_TAG_MIN_COUNT, DEFAULT_AGENT_DESCRIPTION, DEFAULT_AGENT_TYPE, normalizeAgentType, validateAgentTags, validateAgentType, buildInitialAgentMd, validateAgentMdFrontmatter, } from '../aun/aid/agentmd.js';
19
+ import { atomicWriteJson } from '../utils/atomic-write.js';
20
+ import { isHClassPath, isLClassPath, isSameOrDescendant } from '../core/permission/protected-paths.js';
21
+ import { MAIN_CLI_BIN } from '../product.js';
22
+ /** Parse the current Agent's Bootstrap welcome subcommand. */
23
+ export function parseAgentWelcomeArgs(args) {
24
+ const tail = args.at(-2) === '--format' && args.at(-1) === 'json' ? args.slice(0, -2) : args;
25
+ if (tail.length === 1 && tail[0] === 'show')
26
+ return { action: 'show' };
27
+ if (tail.length === 2 && tail[0] === '--text' && typeof tail[1] === 'string') {
28
+ return { action: 'set', text: tail[1] };
29
+ }
30
+ throw new Error(`用法: ${MAIN_CLI_BIN} agent welcome show | --text <text> [--format json]`);
31
+ }
16
32
  export function parseAgentTags(raw) {
17
33
  if (raw === undefined)
18
34
  return undefined;
@@ -89,6 +105,42 @@ function withStaticOwners(config, ownersInput) {
89
105
  function withStaticOwner(config, owner) {
90
106
  return withStaticOwners(config, owner ? [owner] : undefined);
91
107
  }
108
+ /**
109
+ * An Agent owner must be able to receive the new Agent's Bootstrap reply.
110
+ * Contact admission is per local Agent, so materialize the new AID in each
111
+ * locally managed owner's contact book immediately after creation. Human or
112
+ * remote owners have no local contact book and are reported as skipped.
113
+ */
114
+ export async function syncOwnerContactsAfterCreate(createdAid, owners) {
115
+ const synced = [];
116
+ const skipped = [];
117
+ const errors = [];
118
+ for (const ownerAid of [...new Set((owners ?? []).map(value => value.trim()).filter(Boolean))]) {
119
+ if (ownerAid === createdAid)
120
+ continue;
121
+ const ownerConfig = path.join(resolvePaths().agentsDir, ownerAid, 'config.json');
122
+ if (!fs.existsSync(ownerConfig)) {
123
+ skipped.push(ownerAid);
124
+ continue;
125
+ }
126
+ try {
127
+ await addContact({
128
+ selfAid: ownerAid,
129
+ primaryId: createdAid,
130
+ actorId: 'agent-create-owner-sync',
131
+ });
132
+ synced.push(ownerAid);
133
+ }
134
+ catch (error) {
135
+ errors.push(`${ownerAid}: ${error instanceof Error ? error.message : String(error)}`);
136
+ }
137
+ }
138
+ return {
139
+ ...(synced.length > 0 ? { ownerContactsSynced: synced } : {}),
140
+ ...(skipped.length > 0 ? { ownerContactsSkipped: skipped } : {}),
141
+ ...(errors.length > 0 ? { ownerContactSyncErrors: errors } : {}),
142
+ };
143
+ }
92
144
  function pickDefaultBaseagent(available) {
93
145
  return AUTO_BASEAGENT_CANDIDATES.find(candidate => available.includes(candidate)) ?? null;
94
146
  }
@@ -137,13 +189,25 @@ export function resolveAgentCreatePlan(options) {
137
189
  const owner = options.owner?.trim() || (owners === undefined ? daemon.owners?.[0] : undefined);
138
190
  const ownerSource = owners !== undefined || options.owner?.trim() ? 'argument' : owner ? 'daemon' : 'none';
139
191
  const name = options.name?.trim() || metadata.name || projectSlug(project);
140
- const description = options.description?.trim() || metadata.description || DEFAULT_AGENT_DESCRIPTION;
192
+ // description 是可选字段:用户显式传入的必须合规(超长会在校验阶段被拒);
193
+ // 从 package.json 推导出来的超长值直接弃用、回落默认短描述——用户没设过它,
194
+ // 不该因此让创建命令失败。
195
+ const explicitDescription = options.description?.trim();
196
+ const packageDescription = metadata.description
197
+ && Array.from(metadata.description).length <= AGENT_DESCRIPTION_MAX_LENGTH
198
+ ? metadata.description
199
+ : undefined;
200
+ const description = explicitDescription || packageDescription || DEFAULT_AGENT_DESCRIPTION;
201
+ // 大小写不敏感:能识别就收敛成小写;无法识别时保留原值,交给 validateAgentCreatePlan 报错。
202
+ const requestedType = options.type?.trim();
203
+ const type = requestedType ? (normalizeAgentType(requestedType) ?? requestedType) : DEFAULT_AGENT_TYPE;
141
204
  const configExists = isValidAid(aid) && fs.existsSync(path.join(resolvePaths().agentsDir, aid, 'config.json'));
142
205
  return {
143
- aid, project, baseagent, owner, owners, handoff: options.handoff, name, description, tags: options.tags,
206
+ aid, project, baseagent, owner, owners, handoff: options.handoff, name, description, type, tags: options.tags,
144
207
  force: options.force === true, configExists, baseagentSource, ownerSource,
145
208
  nameSource: options.name?.trim() ? 'argument' : metadata.name ? 'package' : 'directory',
146
- descriptionSource: options.description?.trim() ? 'argument' : metadata.description ? 'package' : 'default',
209
+ descriptionSource: explicitDescription ? 'argument' : packageDescription ? 'package' : 'default',
210
+ typeSource: options.type?.trim() ? 'argument' : 'default',
147
211
  };
148
212
  }
149
213
  export function validateAgentCreatePlan(plan) {
@@ -159,6 +223,16 @@ export function validateAgentCreatePlan(plan) {
159
223
  if (tagErrors.length > 0)
160
224
  return `Invalid tags: ${tagErrors.join('; ')}`;
161
225
  }
226
+ const typeError = validateAgentType(plan.type);
227
+ if (typeError)
228
+ return `Invalid --type: ${typeError}`;
229
+ // 只有显式传入的 --description 才会超限(package.json 的超长值在 resolve 阶段
230
+ // 已被弃用)。必须在这里就拒绝:否则 plan 通过、直到 agentmdPut 上传才失败,
231
+ // 而那处是 warn 级不中断创建,最终留下一张 SDK 兜底名片(--type/--name 全部丢失)。
232
+ const descriptionLength = Array.from(plan.description).length;
233
+ if (descriptionLength > AGENT_DESCRIPTION_MAX_LENGTH) {
234
+ return `Invalid --description: ${descriptionLength} characters exceeds the agent.md limit of ${AGENT_DESCRIPTION_MAX_LENGTH}. Pass a shorter --description.`;
235
+ }
162
236
  if (!plan.baseagent)
163
237
  return 'No usable baseagent detected. Pass --baseagent or configure a baseagent in agents/defaults.json.';
164
238
  if (!BASEAGENT_CANDIDATES.includes(plan.baseagent)) {
@@ -219,6 +293,8 @@ export async function agentCreateInteractive(opts = {}) {
219
293
  }
220
294
  aid = candidate;
221
295
  }
296
+ if (hasSuppliedInitialization(aid))
297
+ return { ok: false, error: 'Use agent initialize --resume for this target' };
222
298
  const agentDirPath = path.join(p.agentsDir, aid);
223
299
  const configExists = fs.existsSync(path.join(agentDirPath, 'config.json'));
224
300
  if (configExists) {
@@ -344,6 +420,8 @@ export async function agentCreateInteractive(opts = {}) {
344
420
  ensureInitialRoleRegistry(aid);
345
421
  saveInitialBehavior(aid, baseagent);
346
422
  ensureAgentDirSkeleton(aid);
423
+ if (owner)
424
+ await syncOwnerContactsAfterCreate(aid, [owner]);
347
425
  let agentmdUploaded = false;
348
426
  try {
349
427
  const content = buildInitialAgentMd({ aid, name: agentName, description: agentDescription, tags: agentTags });
@@ -399,7 +477,21 @@ export async function agentCreateInteractive(opts = {}) {
399
477
  ownerBoundAid = await promptAgentOwnerManually(aid);
400
478
  }
401
479
  }
402
- return { ok: true, aid, configPath: toPosix(path.join(agentDirPath, 'config.json')), aidCreated, agentmdUploaded, hotLoaded, hotLoadError, ownerBoundAid, ownerBindSkipped, ownerBindInterrupted };
480
+ const finalOwnerAid = owner ?? ownerBoundAid;
481
+ const ownerContactSync = await syncOwnerContactsAfterCreate(aid, finalOwnerAid ? [finalOwnerAid] : undefined);
482
+ return {
483
+ ok: true,
484
+ aid,
485
+ configPath: toPosix(path.join(agentDirPath, 'config.json')),
486
+ aidCreated,
487
+ agentmdUploaded,
488
+ hotLoaded,
489
+ hotLoadError,
490
+ ownerBoundAid,
491
+ ownerBindSkipped,
492
+ ownerBindInterrupted,
493
+ ...ownerContactSync,
494
+ };
403
495
  }
404
496
  finally {
405
497
  if (ownRl) {
@@ -449,8 +541,21 @@ export async function agentCreateNonInteractive(opts, runtime = {}) {
449
541
  if (tagErrors.length > 0)
450
542
  return failValidating(`Invalid tags: ${tagErrors.join('; ')}`);
451
543
  }
544
+ if (opts.type !== undefined) {
545
+ const typeError = validateAgentType(opts.type);
546
+ if (typeError)
547
+ return failValidating(`Invalid --type: ${typeError}`);
548
+ }
549
+ if (opts.description !== undefined) {
550
+ const descriptionLength = Array.from(opts.description).length;
551
+ if (descriptionLength > AGENT_DESCRIPTION_MAX_LENGTH) {
552
+ return failValidating(`Invalid description: ${descriptionLength} characters exceeds the agent.md limit of ${AGENT_DESCRIPTION_MAX_LENGTH}. Pass a shorter --description.`);
553
+ }
554
+ }
452
555
  if (!isValidAid(opts.aid))
453
556
  return failValidating(`Invalid AID "${opts.aid}": must be a valid multi-level domain (e.g. mybot.agentid.pub)`);
557
+ if (hasSuppliedInitialization(opts.aid))
558
+ return failValidating('Use agent initialize --resume for this target');
454
559
  const agentDirPath = path.join(p.agentsDir, opts.aid);
455
560
  const configExists = fs.existsSync(path.join(agentDirPath, 'config.json'));
456
561
  if (configExists && !opts.force)
@@ -518,11 +623,13 @@ export async function agentCreateNonInteractive(opts, runtime = {}) {
518
623
  ensureInitialRoleRegistry(opts.aid);
519
624
  saveInitialBehavior(opts.aid, baseagent);
520
625
  ensureAgentDirSkeleton(opts.aid);
626
+ const ownerContactSync = await syncOwnerContactsAfterCreate(opts.aid, opts.owners ?? (opts.owner ? [opts.owner] : undefined));
521
627
  opts.onPhase?.('config_saved', 'done');
522
628
  opts.onPhase?.('uploading_agentmd', 'begin');
523
629
  let agentmdUploaded = false;
630
+ let agentmdError;
524
631
  try {
525
- const content = buildInitialAgentMd({ aid: opts.aid, name: opts.name, description: opts.description, tags: opts.tags });
632
+ const content = buildInitialAgentMd({ aid: opts.aid, name: opts.name, description: opts.description, type: opts.type, tags: opts.tags });
526
633
  const aunPath = process.env.AUN_HOME || defaultAunPath();
527
634
  let lastError;
528
635
  for (let attempt = 1; attempt <= 3; attempt++) {
@@ -538,12 +645,15 @@ export async function agentCreateNonInteractive(opts, runtime = {}) {
538
645
  lastError = e;
539
646
  }
540
647
  }
541
- if (!agentmdUploaded)
542
- opts.onPhase?.('uploading_agentmd', 'warn', `upload failed: ${lastError?.message || lastError}`);
648
+ if (!agentmdUploaded) {
649
+ agentmdError = `upload failed: ${lastError?.message || lastError}`;
650
+ opts.onPhase?.('uploading_agentmd', 'warn', agentmdError);
651
+ }
543
652
  await new Promise(r => setTimeout(r, 0));
544
653
  }
545
654
  catch (e) {
546
- opts.onPhase?.('uploading_agentmd', 'warn', `generation failed: ${e?.message || e}`);
655
+ agentmdError = `generation failed: ${e?.message || e}`;
656
+ opts.onPhase?.('uploading_agentmd', 'warn', agentmdError);
547
657
  }
548
658
  let hotLoaded = false;
549
659
  let hotLoadError;
@@ -572,5 +682,510 @@ export async function agentCreateNonInteractive(opts, runtime = {}) {
572
682
  hotLoadError = error instanceof Error ? error.message : String(error);
573
683
  opts.onPhase?.('hot_loading', 'warn', hotLoadError);
574
684
  }
575
- return { ok: true, aid: opts.aid, ownerAid: (opts.owners ?? (opts.owner ? [opts.owner] : undefined))?.[0], configPath: toPosix(path.join(agentDirPath, 'config.json')), aidCreated, agentmdUploaded, hotLoaded, hotLoadError };
685
+ return {
686
+ ok: true,
687
+ aid: opts.aid,
688
+ ownerAid: (opts.owners ?? (opts.owner ? [opts.owner] : undefined))?.[0],
689
+ configPath: toPosix(path.join(agentDirPath, 'config.json')),
690
+ aidCreated,
691
+ agentmdUploaded,
692
+ agentmdError,
693
+ hotLoaded,
694
+ hotLoadError,
695
+ ...ownerContactSync,
696
+ };
697
+ }
698
+ // ---- One-shot Agent initialization (kept with Agent creation) ----
699
+ export const INITIALIZATION_PERSONA_MAX_BYTES = 64 * 1024;
700
+ export function validateInitializationPersonaContent(content) {
701
+ if (content.includes('\0'))
702
+ return 'persona 只能包含文本内容';
703
+ if (Buffer.byteLength(content, 'utf8') > INITIALIZATION_PERSONA_MAX_BYTES) {
704
+ return `persona 不能超过 ${INITIALIZATION_PERSONA_MAX_BYTES} bytes`;
705
+ }
706
+ return null;
707
+ }
708
+ export function initializationPath(aid) {
709
+ return path.join(agentDataDir(aid), 'initialization.json');
710
+ }
711
+ export function initializationFingerprint(plan) {
712
+ return createHash('sha256').update(JSON.stringify([
713
+ plan.aid, plan.project, plan.baseagent, plan.owners, plan.name,
714
+ plan.description, plan.type, plan.tags, plan.persona ?? null,
715
+ ])).digest('hex');
716
+ }
717
+ export function readInitialization(aid) {
718
+ const file = initializationPath(aid);
719
+ if (!fs.existsSync(file))
720
+ return null;
721
+ const record = JSON.parse(fs.readFileSync(file, 'utf8'));
722
+ if (record.version !== 1 || record.plan?.aid !== aid || !/^init-[a-f0-9]{24}$/.test(record.operationId)
723
+ || !['in_progress', 'failed', 'complete', 'cancelled'].includes(record.status)
724
+ || initializationFingerprint(record.plan) !== record.fingerprint) {
725
+ throw new Error('INITIALIZE_STATE_INVALID');
726
+ }
727
+ return record;
728
+ }
729
+ export function saveInitialization(record) {
730
+ const file = initializationPath(record.plan.aid);
731
+ fs.mkdirSync(path.dirname(file), { recursive: true, mode: 0o700 });
732
+ record.updatedAt = Date.now();
733
+ atomicWriteJson(file, record);
734
+ // The hot backup also contains the private plan snapshot.
735
+ for (const target of [file, `${file}_`])
736
+ if (fs.existsSync(target))
737
+ fs.chmodSync(target, 0o600);
738
+ }
739
+ /** Pure summary: never expose the persona, project path or authorization snapshot. */
740
+ export function initializationSummary(aid) {
741
+ try {
742
+ const r = readInitialization(aid);
743
+ if (!r)
744
+ return undefined;
745
+ return {
746
+ mode: 'supplied', operationId: r.operationId, fingerprint: r.fingerprint,
747
+ status: r.status, phase: r.phase, complete: r.status === 'complete',
748
+ code: r.code, error: r.error, retryable: r.status === 'failed',
749
+ nextStep: r.status === 'failed' ? `ec agent initialize ${aid} --resume` : undefined,
750
+ agentmd: { published: r.published === true, verified: r.verified === true },
751
+ persona: { provided: r.plan.persona !== undefined },
752
+ welcome: r.welcome, startedAt: r.startedAt, updatedAt: r.updatedAt,
753
+ };
754
+ }
755
+ catch {
756
+ return { mode: 'supplied', status: 'failed', complete: false, code: 'INITIALIZE_STATE_INVALID' };
757
+ }
758
+ }
759
+ /** Persistent guard also covers a removed/tampered config marker. */
760
+ export function initializationBlocksTasks(config) {
761
+ try {
762
+ const r = readInitialization(config.aid);
763
+ if (!r && config.initialization === undefined)
764
+ return false;
765
+ return !r || config.initialization?.operationId !== r.operationId
766
+ || config.initialization.mode !== 'supplied' || config.lifecycle !== 'active'
767
+ || r.status !== 'complete';
768
+ }
769
+ catch {
770
+ return true;
771
+ }
772
+ }
773
+ export function hasSuppliedInitialization(aid) {
774
+ return fs.existsSync(initializationPath(aid));
775
+ }
776
+ function failure(code, error = code) {
777
+ throw Object.assign(new Error(error), { code });
778
+ }
779
+ export function validateInitializationPlan(plan) {
780
+ if (!plan.name?.trim() || !plan.description?.trim() || !Array.isArray(plan.tags) || !plan.owners?.length) {
781
+ failure('INITIALIZE_FIELDS_REQUIRED', 'Explicit name, description, tags and owners are required');
782
+ }
783
+ if (plan.type !== 'agent')
784
+ failure('INVALID_ARGS', 'Initialized targets must have type=agent');
785
+ const error = validateAgentCreatePlan({
786
+ ...plan, force: false, configExists: false, baseagentSource: 'argument', ownerSource: 'argument',
787
+ nameSource: 'argument', descriptionSource: 'argument', typeSource: 'argument',
788
+ });
789
+ if (error)
790
+ failure('INVALID_ARGS', error);
791
+ if (!path.isAbsolute(plan.project))
792
+ failure('INVALID_ARGS', 'project must be an absolute path');
793
+ if (fs.existsSync(plan.project) && !fs.statSync(plan.project).isDirectory())
794
+ failure('INVALID_ARGS', 'project is not a directory');
795
+ const card = validateAgentMdFrontmatter(buildInitialAgentMd(plan), plan.aid);
796
+ if (!card.valid)
797
+ failure('INVALID_ARGS', card.errors.join('; '));
798
+ if (plan.persona !== undefined) {
799
+ if (typeof plan.persona !== 'string')
800
+ failure('INVALID_ARGS', 'persona must be text');
801
+ const personaError = validateInitializationPersonaContent(plan.persona);
802
+ if (personaError)
803
+ failure('INVALID_ARGS', personaError);
804
+ }
805
+ }
806
+ function cardHash(content) {
807
+ return createHash('sha256').update(content).digest('hex');
808
+ }
809
+ function matchesCard(content, plan) {
810
+ if (!validateAgentMdFrontmatter(content, plan.aid).valid)
811
+ return false;
812
+ const fields = parse(content.match(/^---\r?\n([\s\S]*?)\r?\n---/)?.[1] ?? '');
813
+ return fields?.aid === plan.aid && fields.name === plan.name && fields.description === plan.description
814
+ && fields.type === plan.type && JSON.stringify(fields.tags) === JSON.stringify(plan.tags);
815
+ }
816
+ /** Daemon-owned, durable creation. All public entry points authorize before calling this service. */
817
+ export class AgentInitializationService {
818
+ deps;
819
+ running = new Map();
820
+ constructor(deps) {
821
+ this.deps = deps;
822
+ }
823
+ status(aid) {
824
+ const summary = initializationSummary(aid);
825
+ if (!summary)
826
+ return { ok: false, code: 'NOT_FOUND', error: 'No initialization record', aid };
827
+ const config = loadAgent(aid);
828
+ const complete = summary.complete === true && config?.lifecycle === 'active';
829
+ return {
830
+ ok: summary.status !== 'failed' && summary.status !== 'cancelled', aid, accepted: true,
831
+ ...summary, complete, lifecycle: config?.lifecycle ?? null,
832
+ usable: complete && this.deps.usable(aid), initialization: summary,
833
+ };
834
+ }
835
+ start(plan, actor, dryRun = false) {
836
+ if (this.deps.authorize && !this.deps.authorize(actor))
837
+ failure('NOT_ALLOWED');
838
+ validateInitializationPlan(plan);
839
+ const fingerprint = initializationFingerprint(plan);
840
+ const existing = readInitialization(plan.aid);
841
+ if (existing) {
842
+ if (existing.actor !== actor || existing.fingerprint !== fingerprint)
843
+ failure('INITIALIZE_PLAN_CONFLICT');
844
+ const config = loadAgent(plan.aid);
845
+ if (existing.status === 'complete'
846
+ && (!config || config.initialization?.operationId !== existing.operationId)) {
847
+ failure('INITIALIZE_STATE_INVALID');
848
+ }
849
+ return dryRun ? { ok: true, dryRun: true, aid: plan.aid, fingerprint } : this.status(plan.aid);
850
+ }
851
+ if (fs.existsSync(agentConfig(plan.aid)) || fs.existsSync(aidLocalDir(plan.aid)))
852
+ failure('INITIALIZE_TARGET_EXISTS');
853
+ if (dryRun) {
854
+ const { persona, ...safe } = plan;
855
+ return { ok: true, dryRun: true, aid: plan.aid, plan: safe, fingerprint, persona: { provided: persona !== undefined } };
856
+ }
857
+ const r = {
858
+ version: 1, operationId: `init-${randomBytes(12).toString('hex')}`, actor, plan, fingerprint,
859
+ status: 'in_progress', phase: 'planned', startedAt: Date.now(), updatedAt: Date.now(),
860
+ };
861
+ saveInitialization(r);
862
+ this.launch(r);
863
+ return this.status(plan.aid);
864
+ }
865
+ resume(aid, actor) {
866
+ if (this.deps.authorize && !this.deps.authorize(actor))
867
+ failure('NOT_ALLOWED');
868
+ const r = readInitialization(aid);
869
+ if (!r)
870
+ failure('NOT_FOUND');
871
+ if (r.actor !== actor)
872
+ failure('INITIALIZE_OWNER_MISMATCH');
873
+ if (r.status === 'cancelled')
874
+ failure('INITIALIZE_CANCELLED');
875
+ if (r.status !== 'complete' && !this.running.has(aid)) {
876
+ validateInitializationPlan(r.plan);
877
+ r.status = 'in_progress';
878
+ delete r.error;
879
+ delete r.code;
880
+ saveInitialization(r);
881
+ this.launch(r);
882
+ }
883
+ return this.status(aid);
884
+ }
885
+ async cancel(aid) {
886
+ const r = readInitialization(aid);
887
+ if (!r || r.status === 'complete')
888
+ return;
889
+ r.status = 'cancelled';
890
+ r.code = 'INITIALIZE_CANCELLED';
891
+ saveInitialization(r);
892
+ await this.running.get(aid);
893
+ }
894
+ async settle(aid) { await this.running.get(aid); }
895
+ launch(r) {
896
+ const aid = r.plan.aid;
897
+ const work = Promise.resolve().then(() => this.run(r)).catch(error => {
898
+ // Cancellation wins over a stale worker, including delete while network I/O was pending.
899
+ let current;
900
+ try {
901
+ current = readInitialization(aid);
902
+ }
903
+ catch {
904
+ return;
905
+ }
906
+ if (!current || current.status === 'cancelled')
907
+ return;
908
+ r.status = 'failed';
909
+ r.code = error.code ?? 'INITIALIZE_FAILED';
910
+ // External provider errors may contain credentials or payloads; expose the failing phase instead.
911
+ r.error = `Initialization failed at ${r.phase} (${r.code})`;
912
+ saveInitialization(r);
913
+ }).catch(() => { })
914
+ .finally(() => this.running.delete(aid));
915
+ this.running.set(aid, work);
916
+ }
917
+ check(r) {
918
+ if (this.deps.authorize && !this.deps.authorize(r.actor))
919
+ failure('NOT_ALLOWED');
920
+ const current = readInitialization(r.plan.aid);
921
+ if (!current || current.operationId !== r.operationId || current.status === 'cancelled')
922
+ failure('INITIALIZE_CANCELLED');
923
+ const config = loadAgent(r.plan.aid);
924
+ if (r.configSaved && (!config || config.enabled === false))
925
+ failure('INITIALIZE_TARGET_CHANGED');
926
+ if (config && (config.initialization?.operationId !== r.operationId
927
+ || JSON.stringify(config.owners) !== JSON.stringify(r.plan.owners)
928
+ || config.active_baseagent !== r.plan.baseagent || config.projects?.defaultPath !== r.plan.project)) {
929
+ failure('INITIALIZE_TARGET_CHANGED');
930
+ }
931
+ }
932
+ phase(r, phase) {
933
+ this.check(r);
934
+ r.phase = phase;
935
+ saveInitialization(r);
936
+ }
937
+ async run(r) {
938
+ const p = r.plan;
939
+ this.phase(r, 'registering_aid');
940
+ await this.deps.register(p.aid);
941
+ this.phase(r, 'config_saved');
942
+ fs.mkdirSync(p.project, { recursive: true });
943
+ if (!loadAgent(p.aid)) {
944
+ saveAgent({
945
+ $schema_version: CONFIG_SCHEMA_VERSION, aid: p.aid, enabled: true, lifecycle: 'created',
946
+ initialization: { mode: 'supplied', operationId: r.operationId }, channels: [], owners: p.owners,
947
+ projects: { defaultPath: p.project }, active_baseagent: p.baseagent,
948
+ baseagents: { [p.baseagent]: {} }, chatmode: { private: 'interactive', group: 'proactive', nothuman: 'proactive' },
949
+ });
950
+ }
951
+ r.configSaved = true;
952
+ this.phase(r, 'persona_saved');
953
+ ensureAgentDirSkeleton(p.aid);
954
+ ensureInitialRoleRegistry(p.aid);
955
+ if (p.persona !== undefined) {
956
+ const file = path.join(agentPersonalDir(p.aid), 'persona.md');
957
+ if (fs.existsSync(file) && fs.readFileSync(file, 'utf8') !== p.persona)
958
+ failure('INITIALIZE_PLAN_CONFLICT');
959
+ fs.writeFileSync(file, p.persona, { encoding: 'utf8', mode: 0o600 });
960
+ }
961
+ await syncOwnerContactsAfterCreate(p.aid, p.owners);
962
+ this.phase(r, 'uploading_agentmd');
963
+ let remote;
964
+ if (r.publicationStarted) {
965
+ // Resolve unknown publish outcomes before attempting a second write.
966
+ try {
967
+ remote = await this.deps.fetch(p.aid);
968
+ this.check(r);
969
+ if (!matchesCard(remote.content, p) || !remote.verified) {
970
+ if (r.published || (r.previousCardHash !== 'none' && cardHash(remote.content) !== r.previousCardHash)) {
971
+ failure('INITIALIZE_AGENTMD_VERIFY_FAILED');
972
+ }
973
+ remote = undefined; // The pre-publication card is still remote: retry the same frozen plan.
974
+ }
975
+ }
976
+ catch (error) {
977
+ const code = String(error.code ?? '');
978
+ const message = error instanceof Error ? error.message : String(error);
979
+ if (code !== 'AGENTMD_NOT_FOUND' && !/not found|404|missing/i.test(message)) {
980
+ failure('INITIALIZE_AGENTMD_VERIFY_FAILED');
981
+ }
982
+ remote = undefined;
983
+ }
984
+ }
985
+ else {
986
+ try {
987
+ const previous = await this.deps.fetch(p.aid);
988
+ this.check(r);
989
+ r.previousCardHash = cardHash(previous.content);
990
+ }
991
+ catch (error) {
992
+ const code = String(error.code ?? '');
993
+ const message = error instanceof Error ? error.message : String(error);
994
+ if (code !== 'AGENTMD_NOT_FOUND' && !/not found|404|missing/i.test(message))
995
+ failure('INITIALIZE_AGENTMD_VERIFY_FAILED');
996
+ // A newly registered AID normally has no remote card yet.
997
+ r.previousCardHash = 'none';
998
+ this.check(r);
999
+ }
1000
+ }
1001
+ if (!remote) {
1002
+ r.publicationStarted = true;
1003
+ saveInitialization(r);
1004
+ await this.deps.publish(p.aid, buildInitialAgentMd(p));
1005
+ this.check(r);
1006
+ r.published = true;
1007
+ saveInitialization(r);
1008
+ remote = await this.deps.fetch(p.aid);
1009
+ }
1010
+ this.check(r);
1011
+ if (!remote.verified || !matchesCard(remote.content, p))
1012
+ failure('INITIALIZE_AGENTMD_VERIFY_FAILED');
1013
+ if (!fs.existsSync(agentMdPath(p.aid)) || !matchesCard(fs.readFileSync(agentMdPath(p.aid), 'utf8'), p)) {
1014
+ failure('INITIALIZE_AGENTMD_VERIFY_FAILED');
1015
+ }
1016
+ r.published = true;
1017
+ r.verified = true;
1018
+ this.phase(r, 'hot_loading');
1019
+ const config = loadAgent(p.aid);
1020
+ if (config.lifecycle !== 'active') {
1021
+ config.lifecycle = 'bootstrapping';
1022
+ saveAgent(config);
1023
+ }
1024
+ await this.deps.hotLoad(p.aid);
1025
+ this.phase(r, 'activating');
1026
+ if (p.persona !== undefined && fs.readFileSync(path.join(agentPersonalDir(p.aid), 'persona.md'), 'utf8') !== p.persona) {
1027
+ failure('INITIALIZE_PLAN_CONFLICT');
1028
+ }
1029
+ r.activationPrepared = true;
1030
+ saveInitialization(r);
1031
+ const result = await this.deps.activate(p.aid);
1032
+ this.check(r);
1033
+ if (!result.ok)
1034
+ failure(result.code ?? 'INITIALIZE_WELCOME_UNAVAILABLE');
1035
+ if (loadAgent(p.aid)?.lifecycle !== 'active')
1036
+ failure('INITIALIZE_STATE_INVALID');
1037
+ // Activation is durable even if a post-commit network delivery failed.
1038
+ r.welcome = result.welcome;
1039
+ r.phase = 'active';
1040
+ r.status = 'complete';
1041
+ saveInitialization(r);
1042
+ }
1043
+ }
1044
+ /** Called only after the IPC adapter has validated the local control token. */
1045
+ export function executeLocalInitializationCommand(service, command) {
1046
+ try {
1047
+ if (!Array.isArray(command.argv) || command.argv.some(value => typeof value !== 'string')) {
1048
+ return { ok: false, code: 'INVALID_ARGS', error: 'argv required' };
1049
+ }
1050
+ const argv = command.argv;
1051
+ const parsed = parseInitializationArgs(argv);
1052
+ if (command.poll === true)
1053
+ return service.status(parsed.aid);
1054
+ if (parsed.resume) {
1055
+ const record = readInitialization(parsed.aid);
1056
+ if (!record)
1057
+ return { ok: false, code: 'NOT_FOUND', error: 'No initialization record' };
1058
+ return service.resume(parsed.aid, record.actor);
1059
+ }
1060
+ return service.start(resolveInitializationPlan(argv), 'local-control', parsed.dryRun);
1061
+ }
1062
+ catch (error) {
1063
+ return {
1064
+ ok: false,
1065
+ code: error.code ?? 'INVALID_ARGS',
1066
+ error: error instanceof Error ? error.message : String(error),
1067
+ };
1068
+ }
1069
+ }
1070
+ /** Shared strict grammar for CLI, permission inspection and daemon dispatch. */
1071
+ export function parseInitializationArgs(args) {
1072
+ const resume = args[0] === 'initialize';
1073
+ if ((!resume && args[0] !== 'new') || !isValidAid(args[1] ?? ''))
1074
+ throw new Error('Invalid initialization command or AID');
1075
+ const booleans = new Set(resume ? ['--resume'] : ['--initialize', '--non-interactive', '--dry-run']);
1076
+ const strings = new Set(resume ? ['--wait', '--format'] : [
1077
+ '--project', '--baseagent', '--owner', '--owners', '--name', '--description', '--tags', '--type',
1078
+ '--persona-file', '--persona-text', '--wait', '--format',
1079
+ ]);
1080
+ const seen = new Set();
1081
+ const values = {};
1082
+ for (let i = 2; i < args.length; i++) {
1083
+ const flag = args[i];
1084
+ if (seen.has(flag) || (!strings.has(flag) && !booleans.has(flag)))
1085
+ throw new Error(`Unsupported or repeated option: ${flag}`);
1086
+ seen.add(flag);
1087
+ if (strings.has(flag)) {
1088
+ const value = args[++i];
1089
+ if (value === undefined || value.startsWith('--'))
1090
+ throw new Error(`${flag} requires a value`);
1091
+ values[flag] = value;
1092
+ }
1093
+ }
1094
+ if (!seen.has(resume ? '--resume' : '--initialize'))
1095
+ throw new Error(resume ? '--resume required' : '--initialize required');
1096
+ if (values['--format'] !== undefined && values['--format'] !== 'json')
1097
+ throw new Error('--format must be json');
1098
+ if (seen.has('--persona-file') && seen.has('--persona-text'))
1099
+ throw new Error('persona sources are mutually exclusive');
1100
+ if (!resume) {
1101
+ for (const key of ['--name', '--description', '--tags'])
1102
+ if (!values[key]?.trim())
1103
+ throw new Error(`${key} must be explicitly supplied`);
1104
+ parseAgentOwnerArgs(args);
1105
+ parseAgentTags(values['--tags']);
1106
+ }
1107
+ const wait = values['--wait'] === undefined ? 60 : Number(values['--wait']);
1108
+ if (!Number.isInteger(wait) || wait < 0 || wait > 300)
1109
+ throw new Error('--wait must be an integer from 0 to 300');
1110
+ return { aid: args[1], resume, dryRun: seen.has('--dry-run'), wait, values };
1111
+ }
1112
+ export function resolveInitializationPlan(args) {
1113
+ const parsed = parseInitializationArgs(args);
1114
+ if (parsed.resume)
1115
+ throw new Error('A resume request cannot supply a new plan');
1116
+ const v = parsed.values;
1117
+ if (v['--persona-file'] !== undefined)
1118
+ throw new Error('CLI must submit persona file contents as text');
1119
+ const p = resolveAgentCreatePlan({
1120
+ aid: parsed.aid, project: v['--project'], baseagent: v['--baseagent'], ...parseAgentOwnerArgs(args),
1121
+ name: v['--name'], description: v['--description'], tags: parseAgentTags(v['--tags']), type: v['--type'],
1122
+ });
1123
+ return {
1124
+ aid: p.aid, project: p.project, baseagent: p.baseagent ?? '', owners: p.owners ?? (p.owner ? [p.owner] : []),
1125
+ name: p.name, description: p.description, tags: p.tags, type: p.type,
1126
+ ...(v['--persona-text'] !== undefined ? { persona: v['--persona-text'] } : {}),
1127
+ };
1128
+ }
1129
+ export function readInitializationPersona(file, managed) {
1130
+ const resolved = path.resolve(file);
1131
+ if (!/\.(md|txt)$/i.test(resolved))
1132
+ throw new Error('persona file must be .md or .txt');
1133
+ const stat = fs.lstatSync(resolved);
1134
+ if (!stat.isFile() || stat.isSymbolicLink() || stat.size > 65536)
1135
+ throw new Error('persona must be a regular text file of at most 64 KiB');
1136
+ if (managed) {
1137
+ const real = fs.realpathSync(resolved);
1138
+ const roots = [process.cwd(), process.env.TMPDIR].filter((r) => !!r && path.isAbsolute(r));
1139
+ const allowed = roots.some(root => isSameOrDescendant(real, fs.realpathSync(root)));
1140
+ if (!allowed || isHClassPath(real) || isLClassPath(real))
1141
+ throw new Error('persona file must be inside the project or current TMPDIR');
1142
+ }
1143
+ const text = new TextDecoder('utf-8', { fatal: true }).decode(fs.readFileSync(resolved));
1144
+ const error = validateInitializationPersonaContent(text);
1145
+ if (error)
1146
+ throw new Error(error);
1147
+ return text;
1148
+ }
1149
+ export async function runInitializationCommand(args) {
1150
+ const parsed = parseInitializationArgs(args);
1151
+ const managed = !!process.env.EVOLCORE_SESSION_ID;
1152
+ const argv = [...args];
1153
+ if (parsed.values['--persona-file'] !== undefined) {
1154
+ const at = argv.indexOf('--persona-file');
1155
+ argv.splice(at, 2, '--persona-text', readInitializationPersona(parsed.values['--persona-file'], managed));
1156
+ }
1157
+ if (!parsed.resume) {
1158
+ const at = argv.indexOf('--project');
1159
+ if (at < 0)
1160
+ argv.push('--project', process.cwd());
1161
+ else
1162
+ argv[at + 1] = path.resolve(argv[at + 1]);
1163
+ }
1164
+ const request = async (poll) => {
1165
+ if (managed) {
1166
+ const { runManagedAgentOperation } = await import('./agent.js');
1167
+ const r = await runManagedAgentOperation(poll ? ['show', parsed.aid, '--format', 'json'] : argv);
1168
+ if (!poll || !r.ok || !r.initialization)
1169
+ return r;
1170
+ const init = r.initialization;
1171
+ return { ...init, ok: !['failed', 'cancelled'].includes(init.status), aid: parsed.aid,
1172
+ accepted: true, lifecycle: r.lifecycle, usable: init.complete && r.connection?.status === 'connected' };
1173
+ }
1174
+ return await ipcQuery(resolvePaths().socket, {
1175
+ type: 'evolagent.initialize', argv, poll,
1176
+ }, 10_000) ?? { ok: false, code: 'DAEMON_UNAVAILABLE', error: 'Initialization requires a running daemon' };
1177
+ };
1178
+ let result = await request(false);
1179
+ if (parsed.dryRun)
1180
+ return result;
1181
+ const deadline = Date.now() + parsed.wait * 1000;
1182
+ while (result.ok && !result.complete && result.accepted && Date.now() < deadline) {
1183
+ await new Promise(resolve => setTimeout(resolve, 500));
1184
+ result = await request(true);
1185
+ }
1186
+ if (result.ok && !result.complete && result.accepted)
1187
+ return { ...result, ok: false, code: 'INITIALIZE_PENDING', error: 'Initialization is still running; inspect agent show' };
1188
+ if (result.complete && !result.usable)
1189
+ return { ...result, ok: false, code: 'INITIALIZE_NOT_USABLE', error: 'Initialized; runtime is not currently connected' };
1190
+ return result;
576
1191
  }